1#
2#   irb/context.rb - irb context
3#   	$Release Version: 0.9.6$
4#   	$Revision: 39048 $
5#   	by Keiju ISHITSUKA(keiju@ruby-lang.org)
6#
7# --
8#
9#
10#
11require "irb/workspace"
12require "irb/inspector"
13
14module IRB
15  # A class that wraps the current state of the irb session, including the
16  # configuration of IRB.conf.
17  class Context
18    # Creates a new IRB context.
19    #
20    # The optional +input_method+ argument:
21    #
22    # +nil+::     uses stdin or Readline
23    # +String+::  uses a File
24    # +other+::   uses this as InputMethod
25    def initialize(irb, workspace = nil, input_method = nil, output_method = nil)
26      @irb = irb
27      if workspace
28	@workspace = workspace
29      else
30	@workspace = WorkSpace.new
31      end
32      @thread = Thread.current if defined? Thread
33#      @irb_level = 0
34
35      # copy of default configuration
36      @ap_name = IRB.conf[:AP_NAME]
37      @rc = IRB.conf[:RC]
38      @load_modules = IRB.conf[:LOAD_MODULES]
39
40      @use_readline = IRB.conf[:USE_READLINE]
41      @verbose = IRB.conf[:VERBOSE]
42      @io = nil
43
44      self.inspect_mode = IRB.conf[:INSPECT_MODE]
45      self.math_mode = IRB.conf[:MATH_MODE] if IRB.conf[:MATH_MODE]
46      self.use_tracer = IRB.conf[:USE_TRACER] if IRB.conf[:USE_TRACER]
47      self.use_loader = IRB.conf[:USE_LOADER] if IRB.conf[:USE_LOADER]
48      self.eval_history = IRB.conf[:EVAL_HISTORY] if IRB.conf[:EVAL_HISTORY]
49
50      @ignore_sigint = IRB.conf[:IGNORE_SIGINT]
51      @ignore_eof = IRB.conf[:IGNORE_EOF]
52
53      @back_trace_limit = IRB.conf[:BACK_TRACE_LIMIT]
54
55      self.prompt_mode = IRB.conf[:PROMPT_MODE]
56
57      if IRB.conf[:SINGLE_IRB] or !defined?(IRB::JobManager)
58	@irb_name = IRB.conf[:IRB_NAME]
59      else
60	@irb_name = IRB.conf[:IRB_NAME]+"#"+IRB.JobManager.n_jobs.to_s
61      end
62      @irb_path = "(" + @irb_name + ")"
63
64      case input_method
65      when nil
66	case use_readline?
67	when nil
68	  if (defined?(ReadlineInputMethod) && STDIN.tty? &&
69	      IRB.conf[:PROMPT_MODE] != :INF_RUBY)
70	    @io = ReadlineInputMethod.new
71	  else
72	    @io = StdioInputMethod.new
73	  end
74	when false
75	  @io = StdioInputMethod.new
76	when true
77	  if defined?(ReadlineInputMethod)
78	    @io = ReadlineInputMethod.new
79	  else
80	    @io = StdioInputMethod.new
81	  end
82	end
83
84      when String
85	@io = FileInputMethod.new(input_method)
86	@irb_name = File.basename(input_method)
87	@irb_path = input_method
88      else
89	@io = input_method
90      end
91      self.save_history = IRB.conf[:SAVE_HISTORY] if IRB.conf[:SAVE_HISTORY]
92
93      if output_method
94	@output_method = output_method
95      else
96	@output_method = StdioOutputMethod.new
97      end
98
99      @echo = IRB.conf[:ECHO]
100      if @echo.nil?
101	@echo = true
102      end
103      self.debug_level = IRB.conf[:DEBUG_LEVEL]
104    end
105
106    # The top-level workspace, see WorkSpace#main
107    def main
108      @workspace.main
109    end
110
111    # The toplevel workspace, see #home_workspace
112    attr_reader :workspace_home
113    # WorkSpace in the current context
114    attr_accessor :workspace
115    # The current thread in this context
116    attr_reader :thread
117    # The current input method
118    #
119    # Can be either StdioInputMethod, ReadlineInputMethod, FileInputMethod or
120    # other specified when the context is created. See ::new for more
121    # information on +input_method+.
122    attr_accessor :io
123
124    # Current irb session
125    attr_accessor :irb
126    # A copy of the default <code>IRB.conf[:AP_NAME]</code>
127    attr_accessor :ap_name
128    # A copy of the default <code>IRB.conf[:RC]</code>
129    attr_accessor :rc
130    # A copy of the default <code>IRB.conf[:LOAD_MODULES]</code>
131    attr_accessor :load_modules
132    # Can be either name from <code>IRB.conf[:IRB_NAME]</code>, or the number of
133    # the current job set by JobManager, such as <code>irb#2</code>
134    attr_accessor :irb_name
135    # Can be either the #irb_name surrounded by parenthesis, or the
136    # +input_method+ passed to Context.new
137    attr_accessor :irb_path
138
139    # Whether +Readline+ is enabled or not.
140    #
141    # A copy of the default <code>IRB.conf[:USE_READLINE]</code>
142    #
143    # See #use_readline= for more information.
144    attr_reader :use_readline
145    # A copy of the default <code>IRB.conf[:INSPECT_MODE]</code>
146    attr_reader :inspect_mode
147
148    # A copy of the default <code>IRB.conf[:PROMPT_MODE]</code>
149    attr_reader :prompt_mode
150    # Standard IRB prompt
151    #
152    # See IRB@Customizing+the+IRB+Prompt for more information.
153    attr_accessor :prompt_i
154    # IRB prompt for continuated strings
155    #
156    # See IRB@Customizing+the+IRB+Prompt for more information.
157    attr_accessor :prompt_s
158    # IRB prompt for continuated statement (e.g. immediately after an +if+)
159    #
160    # See IRB@Customizing+the+IRB+Prompt for more information.
161    attr_accessor :prompt_c
162    # See IRB@Customizing+the+IRB+Prompt for more information.
163    attr_accessor :prompt_n
164    # Can be either the default <code>IRB.conf[:AUTO_INDENT]</code>, or the
165    # mode set by #prompt_mode=
166    #
167    # To enable auto-indentation in irb:
168    #
169    #     IRB.conf[:AUTO_INDENT] = true
170    #
171    # or
172    #
173    #     irb_context.auto_indent_mode = true
174    #
175    # or
176    #
177    #     IRB.CurrentContext.auto_indent_mode = true
178    #
179    # See IRB@Configuration for more information.
180    attr_accessor :auto_indent_mode
181    # The format of the return statement, set by #prompt_mode= using the
182    # +:RETURN+ of the +mode+ passed to set the current #prompt_mode.
183    attr_accessor :return_format
184
185    # Whether <code>^C</code> (+control-c+) will be ignored or not.
186    #
187    # If set to +false+, <code>^C</code> will quit irb.
188    #
189    # If set to +true+,
190    #
191    # * during input:   cancel input then return to top level.
192    # * during execute: abandon current execution.
193    attr_accessor :ignore_sigint
194    # Whether <code>^D</code> (+control-d+) will be ignored or not.
195    #
196    # If set to +false+, <code>^D</code> will quit irb.
197    attr_accessor :ignore_eof
198    # Whether to echo the return value to output or not.
199    #
200    # Uses IRB.conf[:ECHO] if available, or defaults to +true+.
201    #
202    #     puts "hello"
203    #     # hello
204    #     #=> nil
205    #     IRB.CurrentContext.echo = false
206    #     puts "omg"
207    #     # omg
208    attr_accessor :echo
209    # Whether verbose messages are displayed or not.
210    #
211    # A copy of the default <code>IRB.conf[:VERBOSE]</code>
212    attr_accessor :verbose
213    # The debug level of irb
214    #
215    # See #debug_level= for more information.
216    attr_reader :debug_level
217
218    # The limit of backtrace lines displayed as top +n+ and tail +n+.
219    #
220    # The default value is 16.
221    #
222    # Can also be set using the +--back-trace-limit+ command line option.
223    #
224    # See IRB@Command+line+options for more command line options.
225    attr_accessor :back_trace_limit
226
227    # Alias for #use_readline
228    alias use_readline? use_readline
229    # Alias for #rc
230    alias rc? rc
231    alias ignore_sigint? ignore_sigint
232    alias ignore_eof? ignore_eof
233    alias echo? echo
234
235    # Returns whether messages are displayed or not.
236    def verbose?
237      if @verbose.nil?
238	if defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod)
239	  false
240	elsif !STDIN.tty? or @io.kind_of?(FileInputMethod)
241	  true
242	else
243	  false
244	end
245      else
246	@verbose
247      end
248    end
249
250    # Whether #verbose? is +true+, and +input_method+ is either
251    # StdioInputMethod or ReadlineInputMethod, see #io for more information.
252    def prompting?
253      verbose? || (STDIN.tty? && @io.kind_of?(StdioInputMethod) ||
254		(defined?(ReadlineInputMethod) && @io.kind_of?(ReadlineInputMethod)))
255    end
256
257    # The return value of the last statement evaluated.
258    attr_reader :last_value
259
260    # Sets the return value from the last statement evaluated in this context
261    # to #last_value.
262    def set_last_value(value)
263      @last_value = value
264      @workspace.evaluate self, "_ = IRB.CurrentContext.last_value"
265    end
266
267    # Sets the +mode+ of the prompt in this context.
268    #
269    # See IRB@Customizing+the+IRB+Prompt for more information.
270    def prompt_mode=(mode)
271      @prompt_mode = mode
272      pconf = IRB.conf[:PROMPT][mode]
273      @prompt_i = pconf[:PROMPT_I]
274      @prompt_s = pconf[:PROMPT_S]
275      @prompt_c = pconf[:PROMPT_C]
276      @prompt_n = pconf[:PROMPT_N]
277      @return_format = pconf[:RETURN]
278      if ai = pconf.include?(:AUTO_INDENT)
279	@auto_indent_mode = ai
280      else
281	@auto_indent_mode = IRB.conf[:AUTO_INDENT]
282      end
283    end
284
285    # Whether #inspect_mode is set or not, see #inspect_mode= for more detail.
286    def inspect?
287      @inspect_mode.nil? or @inspect_mode
288    end
289
290    # Whether #io uses a File for the +input_method+ passed when creating the
291    # current context, see ::new
292    def file_input?
293      @io.class == FileInputMethod
294    end
295
296    # Specifies the inspect mode with +opt+:
297    #
298    # +true+::  display +inspect+
299    # +false+:: display +to_s+
300    # +nil+::   inspect mode in non-math mode,
301    #           non-inspect mode in math mode
302    #
303    # See IRB::Inspector for more information.
304    #
305    # Can also be set using the +--inspect+ and +--noinspect+ command line
306    # options.
307    #
308    # See IRB@Command+line+options for more command line options.
309    def inspect_mode=(opt)
310
311      if i = Inspector::INSPECTORS[opt]
312	@inspect_mode = opt
313	@inspect_method = i
314	i.init
315      else
316	case opt
317	when nil
318	  if Inspector.keys_with_inspector(Inspector::INSPECTORS[true]).include?(@inspect_mode)
319	    self.inspect_mode = false
320	  elsif Inspector.keys_with_inspector(Inspector::INSPECTORS[false]).include?(@inspect_mode)
321	    self.inspect_mode = true
322	  else
323	    puts "Can't switch inspect mode."
324	    return
325	  end
326	when /^\s*\{.*\}\s*$/
327	  begin
328	    inspector = eval "proc#{opt}"
329	  rescue Exception
330	    puts "Can't switch inspect mode(#{opt})."
331	    return
332	  end
333	  self.inspect_mode = inspector
334	when Proc
335	  self.inspect_mode = IRB::Inspector(opt)
336	when Inspector
337	  prefix = "usr%d"
338	  i = 1
339	  while Inspector::INSPECTORS[format(prefix, i)]; i += 1; end
340	  @inspect_mode = format(prefix, i)
341	  @inspect_method = opt
342	  Inspector.def_inspector(format(prefix, i), @inspect_method)
343	else
344	  puts "Can't switch inspect mode(#{opt})."
345	  return
346	end
347      end
348      print "Switch to#{unless @inspect_mode; ' non';end} inspect mode.\n" if verbose?
349      @inspect_mode
350    end
351
352    # Obsolete method.
353    #
354    # Can be set using the +--noreadline+ and +--readline+ command line
355    # options.
356    #
357    # See IRB@Command+line+options for more command line options.
358    def use_readline=(opt)
359      print "This method is obsolete."
360      print "Do nothing."
361    end
362
363    # Sets the debug level of irb
364    #
365    # Can also be set using the +--irb_debug+ command line option.
366    #
367    # See IRB@Command+line+options for more command line options.
368    def debug_level=(value)
369      @debug_level = value
370      RubyLex.debug_level = value
371    end
372
373    # Whether or not debug mode is enabled, see #debug_level=.
374    def debug?
375      @debug_level > 0
376    end
377
378    def evaluate(line, line_no) # :nodoc:
379      @line_no = line_no
380      set_last_value(@workspace.evaluate(self, line, irb_path, line_no))
381#      @workspace.evaluate("_ = IRB.conf[:MAIN_CONTEXT]._")
382#      @_ = @workspace.evaluate(line, irb_path, line_no)
383    end
384
385    def inspect_last_value # :nodoc:
386      @inspect_method.inspect_value(@last_value)
387    end
388
389    alias __exit__ exit
390    # Exits the current session, see IRB.irb_exit
391    def exit(ret = 0)
392      IRB.irb_exit(@irb, ret)
393    end
394
395    NOPRINTING_IVARS = ["@last_value"] # :nodoc:
396    NO_INSPECTING_IVARS = ["@irb", "@io"] # :nodoc:
397    IDNAME_IVARS = ["@prompt_mode"] # :nodoc:
398
399    alias __inspect__ inspect
400    def inspect # :nodoc:
401      array = []
402      for ivar in instance_variables.sort{|e1, e2| e1 <=> e2}
403	ivar = ivar.to_s
404	name = ivar.sub(/^@(.*)$/, '\1')
405	val = instance_eval(ivar)
406	case ivar
407	when *NOPRINTING_IVARS
408	  array.push format("conf.%s=%s", name, "...")
409	when *NO_INSPECTING_IVARS
410	  array.push format("conf.%s=%s", name, val.to_s)
411	when *IDNAME_IVARS
412	  array.push format("conf.%s=:%s", name, val.id2name)
413	else
414	  array.push format("conf.%s=%s", name, val.inspect)
415	end
416      end
417      array.join("\n")
418    end
419    alias __to_s__ to_s
420    alias to_s inspect
421  end
422end
423