Class: Relisp::Slave
- Defined in:
- lib/relisp/slaves.rb,
lib/relisp/elisp_functions.rb
Overview
This is the base class for RubySlave and ElispSlave–the Slave class isn’t meant to be used itself.
Direct Known Subclasses
Constant Summary collapse
- CONSTANTS =
Ruby and elisp use these strings to terminate messages sent to each other. This ruby library and the elisp code are tied to one another so closely that I don’t know if it matters, but it still seemed like a bad idea to hard code the constants in both places. Just make sure that the order in the elisp function
relisp-get-constantsmatches the ruby methodsend_constants. Array.new
- TRANSMISSION_CODES_REGEXP =
Regexp.new(CONSTANTS.join('|'))
- PREVIOUS_ELISP_RESULT =
Every time ruby asks elisp to evaluate an expression, the result is saved in this variable so ruby can access it if necessary.
:"--relisp--previous--result"- VARIABLE_PREFIX =
A prefix for elisp variables created by ruby.
'--relisp--variable--'
Instance Method Summary collapse
-
#elisp_eval(code) ⇒ Object
Run code in the elisp process and return the result as the corresponding ruby object.
-
#elisp_exec(code) ⇒ Object
Run code in the elisp process.
-
#get_permanent_variable(old_variable) ⇒ Object
Return a symbol corresponding to a new elisp variable which hold the same information as old_variable.
-
#initialize ⇒ Slave
constructor
A new instance of Slave.
-
#new_elisp_variable ⇒ Object
Return a symbol which corresponds to an unused variable in elisp.
-
#provide(symbol, binding) ⇒ Object
Creates a method in the slave that is a reference to the variable given by symbol in the scope of binding.
-
#save_excursion ⇒ Object
Save point, mark, and current buffer; execute a block of code; restore those things.
-
#with_current_buffer ⇒ Object
Save the current buffer; execute a block of code; restore the current buffer.
-
#with_temp_buffer ⇒ Object
Create a temporary buffer, and evaluate a block of code there.
Constructor Details
#initialize ⇒ Slave
Returns a new instance of Slave.
67 68 69 70 71 72 73 74 75 76 |
# File 'lib/relisp/slaves.rb', line 67 def initialize # Whenever ruby calls 'eval' on some code at the request of # elisp it is in a context where any new variables set will drop # out of scope immediately. The @local_binding is a way of # allowing these variables to persist through multiple calls. @local_binding = binding @current_elisp_variable_num = '0' @debug = nil Relisp.default_slave = self end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(function, *args) ⇒ Object (private)
Forward any missing method to elisp.
If a function with that name exists, write the rubyized (swap _ for -) function and arguments in prefix notation (calling the to_elisp method of each of the args).
If a symbol with that name exists, return its value.
If the last char of the missing method is ‘=’, set the symbol formed by the characters before the ‘=’ to the first argument.
For example:
emacs = Relisp::ElispSlave.new
puts emacs.concat "two", "words"
emacs.a= 5
puts emacs.a
This automatically allows access to a large portion of elisp in a rubyish way.
107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/relisp/elisp_functions.rb', line 107 def method_missing(function, *args) # :doc: lisp_name = function.to_s.gsub('_', '-') if elisp_eval "(functionp '#{lisp_name})" elisp_eval "(#{lisp_name} #{args.map{|a| a.to_elisp}.join(' ')})" elsif elisp_eval("(boundp '#{lisp_name})") && args.empty? # if there are args, it was meant to be a function elisp_eval "#{lisp_name}" elsif lisp_name[lisp_name.size-1..lisp_name.size-1] == "=" elisp_eval "(setq #{lisp_name[0..lisp_name.size-2]} #{args[0].to_elisp})" else raise NameError, "#{function} is undefined variable/method/function in Ruby and Elisp" end # elisp_eval("(if (functionp '#{function}) (#{function} #{args.map{|a| a.to_elisp}.join(' ')}) #{function})") end |
Instance Method Details
#elisp_eval(code) ⇒ Object
Run code in the elisp process and return the result as the corresponding ruby object. If the ruby object is not going to be used, use elisp_exec instead.
108 109 110 111 112 113 |
# File 'lib/relisp/slaves.rb', line 108 def elisp_eval(code) code = code.to_s # maybe code is a symbol or something write_to_emacs code write_to_emacs QUESTION_CODE to_ruby(receive_answer) end |
#elisp_exec(code) ⇒ Object
Run code in the elisp process.
97 98 99 100 101 102 |
# File 'lib/relisp/slaves.rb', line 97 def elisp_exec(code) code = code.to_s # maybe code is nil or something write_to_emacs code write_to_emacs COMMAND_CODE receive_answer end |
#get_permanent_variable(old_variable) ⇒ Object
Return a symbol corresponding to a new elisp variable which hold the same information as old_variable. Intended primarily for use in the from_elisp method in various classes.
89 90 91 92 93 |
# File 'lib/relisp/slaves.rb', line 89 def get_permanent_variable(old_variable) permanent_variable = new_elisp_variable elisp_exec( "(setq #{permanent_variable} #{old_variable})" ) return permanent_variable end |
#new_elisp_variable ⇒ Object
Return a symbol which corresponds to an unused variable in elisp.
81 82 83 |
# File 'lib/relisp/slaves.rb', line 81 def new_elisp_variable (VARIABLE_PREFIX + @current_elisp_variable_num.succ!).to_sym end |
#provide(symbol, binding) ⇒ Object
Creates a method in the slave that is a reference to the variable given by symbol in the scope of binding. This is probably only useful when calling elisp in ruby where the elisp code itself calls ruby again. When the elisp process calls ruby_eval the code is executed inside the loop of the slave object, so the variables in the scope of the original call to elisp aren’t normally available.
emacs = Relisp::ElispSlave.new
number = 5
emacs.elisp_eval('(ruby-eval "number")') # => [error]
emacs = Relisp::ElispSlave.new
number = 5
local_binding = binding
emacs.provide(:number, local_binding)
emacs.elisp_eval('(ruby-eval "number")') # => 5
216 217 218 219 220 221 222 223 224 |
# File 'lib/relisp/slaves.rb', line 216 def provide(symbol, binding) instance_variable_set "@__#{symbol.to_s}__binding".to_sym, binding instance_eval " def \#{symbol.to_s}\n eval(\"\#{symbol.to_s}\", @__\#{symbol.to_s}__binding)\n end\n endstr\nend\n" |
#save_excursion ⇒ Object
Save point, mark, and current buffer; execute a block of code; restore those things.
This does not simply call the save-excursion function in elisp, it is a rewrite to accept a ruby block.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/relisp/elisp_functions.rb', line 32 def save_excursion raise ArgumentError unless block_given? begin start_point = point() start_mark = mark() start_buffer = current_buffer() start_active = elisp_eval( "mark-active" ) yield ensure set_buffer(start_buffer) set(:"mark-active", start_active) goto_char(start_point) set_mark(start_mark) end end |
#with_current_buffer ⇒ Object
Save the current buffer; execute a block of code; restore the current buffer.
This does not simply call the with-current-buffer function in elisp, it is a rewrite to accept a ruby block.
54 55 56 57 58 59 60 61 62 |
# File 'lib/relisp/elisp_functions.rb', line 54 def with_current_buffer raise ArgumentError unless block_given? begin start_buffer = current_buffer() yield ensure set_buffer(start_buffer) end end |
#with_temp_buffer ⇒ Object
Create a temporary buffer, and evaluate a block of code there.
This does not simply call the with-temp-buffer function in elisp, it is a rewrite to accept a ruby block.
69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/relisp/elisp_functions.rb', line 69 def with_temp_buffer raise ArgumentError unless block_given? begin start_buffer = current_buffer() temp_buffer = Relisp::Buffer.new("*temp--relisp--buffer*", self) yield ensure set_buffer(start_buffer) temp_buffer.kill end end |