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-constants
matches 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.
-
#elisp_function?(name) ⇒ Boolean
TODO: save_selected_window TODO: with_selected_window.
-
#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 77 78 79 |
# 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 @elisp_function_eh ||= Hash.new # for memoization 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.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/relisp/elisp_functions.rb', line 118 def method_missing(function, *args) # :doc: lisp_name = function.to_s.gsub('_', '-') if elisp_function? 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.
111 112 113 114 115 116 |
# File 'lib/relisp/slaves.rb', line 111 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.
100 101 102 103 104 105 |
# File 'lib/relisp/slaves.rb', line 100 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 |
#elisp_function?(name) ⇒ Boolean
TODO: save_selected_window TODO: with_selected_window
88 89 90 91 |
# File 'lib/relisp/elisp_functions.rb', line 88 def elisp_function?(name) @elisp_function_eh[name] ||= elisp_eval "(functionp '#{name})" 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.
92 93 94 95 96 |
# File 'lib/relisp/slaves.rb', line 92 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.
84 85 86 |
# File 'lib/relisp/slaves.rb', line 84 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
219 220 221 222 223 224 225 226 227 |
# File 'lib/relisp/slaves.rb', line 219 def provide(symbol, binding) instance_variable_set "@__#{symbol.to_s}__binding".to_sym, binding instance_eval <<-endstr def #{symbol.to_s} eval("#{symbol.to_s}", @__#{symbol.to_s}__binding) end endstr end |
#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.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/relisp/elisp_functions.rb', line 36 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.
58 59 60 61 62 63 64 65 66 |
# File 'lib/relisp/elisp_functions.rb', line 58 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.
73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/relisp/elisp_functions.rb', line 73 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 |