├── .github
├── ISSUE_TEMPLATE.md
├── dependabot.yml
├── release.yml
└── workflows
│ ├── gh-pages.yml
│ ├── push_gem.yml
│ ├── test.yml
│ └── truffle-ruby-test.yml
├── .gitignore
├── .rdoc_options
├── .rubocop.yml
├── CONTRIBUTING.md
├── EXTEND_IRB.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── bin
├── console
└── setup
├── doc
├── .document
├── COMMAND_LINE_OPTIONS.md
├── COMPARED_WITH_PRY.md
├── Configurations.md
├── EXTEND_IRB.md
├── Index.md
└── irb
│ ├── irb-tools.rd.ja
│ └── irb.rd.ja
├── exe
└── irb
├── irb.gemspec
├── lib
├── .document
├── irb.rb
└── irb
│ ├── .document
│ ├── cmd
│ └── nop.rb
│ ├── color.rb
│ ├── color_printer.rb
│ ├── command.rb
│ ├── command
│ ├── backtrace.rb
│ ├── base.rb
│ ├── break.rb
│ ├── catch.rb
│ ├── cd.rb
│ ├── chws.rb
│ ├── context.rb
│ ├── continue.rb
│ ├── copy.rb
│ ├── debug.rb
│ ├── delete.rb
│ ├── disable_irb.rb
│ ├── edit.rb
│ ├── exit.rb
│ ├── finish.rb
│ ├── force_exit.rb
│ ├── help.rb
│ ├── history.rb
│ ├── info.rb
│ ├── internal_helpers.rb
│ ├── irb_info.rb
│ ├── load.rb
│ ├── ls.rb
│ ├── measure.rb
│ ├── next.rb
│ ├── pushws.rb
│ ├── show_doc.rb
│ ├── show_source.rb
│ ├── step.rb
│ ├── subirb.rb
│ └── whereami.rb
│ ├── completion.rb
│ ├── context.rb
│ ├── debug.rb
│ ├── debug
│ └── ui.rb
│ ├── default_commands.rb
│ ├── easter-egg.rb
│ ├── ext
│ ├── change-ws.rb
│ ├── eval_history.rb
│ ├── loader.rb
│ ├── multi-irb.rb
│ ├── tracer.rb
│ ├── use-loader.rb
│ └── workspaces.rb
│ ├── frame.rb
│ ├── help.rb
│ ├── helper_method.rb
│ ├── helper_method
│ ├── base.rb
│ └── conf.rb
│ ├── history.rb
│ ├── init.rb
│ ├── input-method.rb
│ ├── inspector.rb
│ ├── lc
│ ├── error.rb
│ ├── help-message
│ └── ja
│ │ ├── error.rb
│ │ └── help-message
│ ├── locale.rb
│ ├── nesting_parser.rb
│ ├── notifier.rb
│ ├── output-method.rb
│ ├── pager.rb
│ ├── ruby-lex.rb
│ ├── ruby_logo.aa
│ ├── source_finder.rb
│ ├── statement.rb
│ ├── version.rb
│ ├── workspace.rb
│ ├── ws-for-case-2.rb
│ └── xmp.rb
├── man
└── irb.1
└── test
├── irb
├── command
│ ├── test_cd.rb
│ ├── test_command_aliasing.rb
│ ├── test_copy.rb
│ ├── test_custom_command.rb
│ ├── test_disable_irb.rb
│ ├── test_force_exit.rb
│ ├── test_help.rb
│ ├── test_multi_irb_commands.rb
│ └── test_show_source.rb
├── helper.rb
├── test_color.rb
├── test_color_printer.rb
├── test_command.rb
├── test_completion.rb
├── test_context.rb
├── test_debugger_integration.rb
├── test_eval_history.rb
├── test_evaluation.rb
├── test_helper_method.rb
├── test_history.rb
├── test_init.rb
├── test_input_method.rb
├── test_irb.rb
├── test_locale.rb
├── test_nesting_parser.rb
├── test_option.rb
├── test_pager.rb
├── test_raise_exception.rb
├── test_ruby_lex.rb
├── test_tracer.rb
├── test_type_completor.rb
├── test_workspace.rb
└── yamatanooroti
│ └── test_rendering.rb
└── lib
└── helper.rb
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 | What are your expected behavior and actual behavior of your environment?
4 |
5 | ## Result of irb_info
6 |
7 | Please paste the result of `irb_info` command in IRB.
8 |
9 | ex.)
10 |
11 | ```
12 | irb(main):001:0> irb_info
13 | =>
14 | Ruby version: 3.0.0
15 | IRB version: irb 1.2.7 (2020-09-19)
16 | InputMethod: RelineInputMethod with Reline 0.1.9 and /home/aycabta/.inputrc
17 | .irbrc path: /home/aycabta/.irbrc
18 | ```
19 |
20 | ## Terminal Emulator
21 |
22 | What's your terminal emulator?
23 |
24 | ## Setting Files
25 |
26 | Are you using `~/.irbrc` and `~/.inputrc`?
27 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: 'github-actions'
4 | directory: '/'
5 | schedule:
6 | interval: 'weekly'
7 |
--------------------------------------------------------------------------------
/.github/release.yml:
--------------------------------------------------------------------------------
1 | # release.yml
2 |
3 | changelog:
4 | categories:
5 | - title: ✨ Enhancements
6 | labels:
7 | - enhancement
8 | - title: 🐛 Bug Fixes
9 | labels:
10 | - bug
11 | - title: 📚 Documentation
12 | labels:
13 | - documentation
14 | - title: 🛠 Other Changes
15 | labels:
16 | - "*"
17 |
--------------------------------------------------------------------------------
/.github/workflows/gh-pages.yml:
--------------------------------------------------------------------------------
1 | name: Deploy IRB documentation to GitHub Pages
2 |
3 | on:
4 | push:
5 | branches: ["master"]
6 | workflow_dispatch:
7 |
8 | permissions:
9 | contents: read
10 | pages: write
11 | id-token: write
12 |
13 | concurrency:
14 | group: "pages"
15 | cancel-in-progress: true
16 |
17 | jobs:
18 | build:
19 | runs-on: ubuntu-latest
20 | if: ${{ github.repository == 'ruby/irb' && !startsWith(github.event_name, 'pull') }}
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@v4
24 | - name: Setup Ruby
25 | uses: ruby/setup-ruby@f26937343756480a8cb3ae1f623b9c8d89ed6984 # v1.196.0
26 | with:
27 | ruby-version: "3.3"
28 | bundler-cache: true
29 | - name: Setup Pages
30 | id: pages
31 | uses: actions/configure-pages@v5
32 | - name: Build with IRB
33 | run: bundle exec rake rdoc
34 | - name: Upload artifact
35 | uses: actions/upload-pages-artifact@v3
36 |
37 | deploy:
38 | environment:
39 | name: github-pages
40 | url: ${{ steps.deployment.outputs.page_url }}
41 | runs-on: ubuntu-latest
42 | needs: build
43 | steps:
44 | - name: Deploy to GitHub Pages
45 | id: deployment
46 | uses: actions/deploy-pages@v4
47 |
--------------------------------------------------------------------------------
/.github/workflows/push_gem.yml:
--------------------------------------------------------------------------------
1 | name: Publish gem to rubygems.org
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*'
7 |
8 | permissions:
9 | contents: read
10 |
11 | jobs:
12 | push:
13 | if: github.repository == 'ruby/irb'
14 | runs-on: ubuntu-latest
15 |
16 | environment:
17 | name: rubygems.org
18 | url: https://rubygems.org/gems/irb
19 |
20 | permissions:
21 | contents: write
22 | id-token: write
23 |
24 | steps:
25 | - name: Harden Runner
26 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0
27 | with:
28 | egress-policy: audit
29 |
30 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
31 |
32 | - name: Set up Ruby
33 | uses: ruby/setup-ruby@13e7a03dc3ac6c3798f4570bfead2aed4d96abfb # v1.244.0
34 | with:
35 | bundler-cache: true
36 | ruby-version: "ruby"
37 |
38 | - name: Publish to RubyGems
39 | uses: rubygems/release-gem@a25424ba2ba8b387abc8ef40807c2c85b96cbe32 # v1.1.1
40 |
41 | - name: Create GitHub release
42 | run: |
43 | tag_name="$(git describe --tags --abbrev=0)"
44 | gh release create "${tag_name}" --verify-tag --generate-notes
45 | env:
46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
47 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: build
2 |
3 | on:
4 | push:
5 | pull_request:
6 | schedule:
7 | - cron: "30 14 * * *"
8 |
9 | jobs:
10 | ruby-versions:
11 | if: github.repository == 'ruby/irb' || github.event_name != 'schedule'
12 | uses: ruby/actions/.github/workflows/ruby_versions.yml@master
13 | with:
14 | engine: cruby
15 | min_version: 2.7
16 | lint:
17 | if: github.repository == 'ruby/irb' || github.event_name != 'schedule'
18 | runs-on: ubuntu-latest
19 | steps:
20 | - uses: actions/checkout@v4
21 | - name: Set up Ruby
22 | uses: ruby/setup-ruby@v1
23 | with:
24 | ruby-version: "3.3"
25 | bundler-cache: true
26 | - name: Run rubocop
27 | run: bundle exec rubocop
28 | irb:
29 | needs: ruby-versions
30 | name: rake test ${{ matrix.ruby }} ${{ matrix.with_latest_reline && '(latest reline)' || '' }}
31 | strategy:
32 | matrix:
33 | ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
34 | with_latest_reline: [true, false]
35 | fail-fast: false
36 | runs-on: ubuntu-latest
37 | env:
38 | WITH_LATEST_RELINE: ${{matrix.with_latest_reline}}
39 | timeout-minutes: 30
40 | steps:
41 | - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
42 | - name: Set up Ruby
43 | uses: ruby/setup-ruby@v1
44 | with:
45 | ruby-version: ${{ matrix.ruby }}
46 | bundler-cache: true
47 | # Added to make Ruby 2.7 correctly require installed default gems, like `pp`.
48 | rubygems: latest
49 | - name: Run tests
50 | run: bundle exec rake test
51 | - name: Run tests in isolation
52 | run: bundle exec rake test_in_isolation
53 | debug-test:
54 | if: github.repository == 'ruby/irb' || github.event_name != 'schedule'
55 | name: Debug compatibility test
56 | runs-on: ubuntu-latest
57 | timeout-minutes: 30
58 | steps:
59 | - uses: actions/checkout@v4
60 | - name: Set up Ruby
61 | uses: ruby/setup-ruby@v1
62 | with:
63 | ruby-version: 3.3
64 | - name: Install dependencies
65 | run: bundle install
66 | - name: Install IRB
67 | run: |
68 | rake install
69 | - name: Download ruby/debug
70 | run: |
71 | git clone https://github.com/ruby/debug
72 | - name: Run debug tests
73 | working-directory: ./debug
74 | run: |
75 | bundle install
76 | bundle exec rake
77 | vterm-yamatanooroti:
78 | needs: ruby-versions
79 | name: >-
80 | vterm-yamatanooroti ${{ matrix.ruby }} ${{ matrix.with_latest_reline && '(latest reline)' || '' }}
81 | runs-on: ubuntu-latest
82 | strategy:
83 | matrix:
84 | ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
85 | with_latest_reline: [true, false]
86 | fail-fast: false
87 | env:
88 | WITH_LATEST_RELINE: ${{matrix.with_latest_reline}}
89 | WITH_VTERM: 1
90 | timeout-minutes: 30
91 | steps:
92 | - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
93 | - name: Install libvterm
94 | run: |
95 | sudo apt install -y libtool-bin
96 | wget http://www.leonerd.org.uk/code/libvterm/libvterm-0.1.4.tar.gz
97 | tar xvzf libvterm-0.1.4.tar.gz
98 | cd libvterm-0.1.4
99 | sed -i -e 's/^PREFIX=.*$/PREFIX=\/usr/g' Makefile
100 | make
101 | sudo make install
102 | - name: Set up Ruby
103 | uses: ruby/setup-ruby@v1
104 | with:
105 | ruby-version: ${{ matrix.ruby }}
106 | bundler-cache: true
107 | - name: rake test_yamatanooroti
108 | run: bundle exec rake test_yamatanooroti
109 |
--------------------------------------------------------------------------------
/.github/workflows/truffle-ruby-test.yml:
--------------------------------------------------------------------------------
1 | name: build-with-truffleruby-head
2 |
3 | on:
4 | push:
5 | pull_request:
6 | schedule:
7 | - cron: "30 14 * * *"
8 |
9 | jobs:
10 | irb:
11 | if: github.repository == 'ruby/irb' || github.event_name != 'schedule'
12 | name: rake test truffleruby-head ${{ matrix.with_latest_reline && '(latest reline)' || '' }}
13 | strategy:
14 | matrix:
15 | with_latest_reline: [true, false]
16 | fail-fast: false
17 | runs-on: ubuntu-latest
18 | env:
19 | WITH_LATEST_RELINE: ${{matrix.with_latest_reline}}
20 | timeout-minutes: 30
21 | steps:
22 | - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
23 | - name: Set up Ruby
24 | uses: ruby/setup-ruby@v1
25 | with:
26 | ruby-version: truffleruby-head
27 | bundler-cache: true
28 | - name: Run tests
29 | run: bundle exec rake test
30 | - name: Run tests in isolation
31 | run: bundle exec rake test_in_isolation
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.bundle/
2 | /.yardoc
3 | /_yardoc/
4 | /coverage/
5 | /pkg/
6 | /spec/reports/
7 | /tmp/
8 | Gemfile.lock
9 | _site/
10 |
--------------------------------------------------------------------------------
/.rdoc_options:
--------------------------------------------------------------------------------
1 | page_dir: doc
2 | warn_missing_rdoc_ref: true
3 |
4 | autolink_excluded_words:
5 | - IRB
6 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | AllCops:
2 | TargetRubyVersion: 3.0
3 | DisabledByDefault: true
4 | SuggestExtensions: false
5 |
6 | Layout/TrailingWhitespace:
7 | Enabled: true
8 |
9 | Layout/TrailingEmptyLines:
10 | Enabled: true
11 |
12 | Layout/IndentationConsistency:
13 | Enabled: true
14 |
15 | Layout/CaseIndentation:
16 | Enabled: true
17 | EnforcedStyle: end
18 |
19 | Layout/CommentIndentation:
20 | Enabled: true
21 |
22 | Layout/IndentationStyle:
23 | Enabled: true
24 |
25 | Layout/SpaceAroundKeyword:
26 | Enabled: true
27 |
28 | Layout/SpaceBeforeComma:
29 | Enabled: true
30 |
31 | Layout/SpaceAfterComma:
32 | Enabled: true
33 |
34 | Style/ClassMethodsDefinitions:
35 | Enabled: true
36 | EnforcedStyle: self_class
37 |
38 | Layout/EmptyLines:
39 | Enabled: true
40 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to IRB
2 |
3 | Bug reports and pull requests are welcome on GitHub at [https://github.com/ruby/irb](https://github.com/ruby/irb).
4 |
5 | ## Set up the environment
6 |
7 | 1. Fork the project to your GitHub account.
8 | 2. Clone the fork with `git clone git@github.com:[your_username]/irb.git`.
9 | 3. Run `bundle install`.
10 | 4. Run `bundle exec rake` to make sure tests pass locally.
11 |
12 | ## Run integration tests
13 |
14 | If your changes affect component rendering, such as the autocompletion's dialog/dropdown, you may need to run IRB's integration tests, known as `yamatanooroti`.
15 |
16 | Before running these tests, ensure that you have `libvterm` installed. If you're using Homebrew, you can install it by running:
17 |
18 | ```bash
19 | brew install libvterm
20 | ```
21 |
22 | After installing `libvterm`, you can run the integration tests using the following commands:
23 |
24 | ```bash
25 | WITH_VTERM=1 bundle install
26 | WITH_VTERM=1 bundle exec rake test test_yamatanooroti
27 | ```
28 |
29 | ## Update documentation
30 |
31 | IRB's documentation is generated by [RDoc](https://ruby.github.io/rdoc/) and published to [ruby.github.io/irb](https://ruby.github.io/irb/). Most of the documentation source lives under the `doc/` directory.
32 |
33 | Run the following command to generate the documentation site locally.
34 |
35 | ```bash
36 | bundle exec rake rdoc
37 | bundle exec rake rerdoc # to force regeneration
38 | ```
39 |
40 | Follow the output message to open the documentation site in your browser.
41 |
42 | > [!Note]
43 | >
44 | > Please ensure that the changes are rendered correctly on the documentation site.
45 | > RDoc's Markdown support is limited, so the rendered result on GitHub might differ from what’s rendered on [https://ruby.github.io/irb](https://ruby.github.io/irb).
46 |
47 | We welcome any improvements to the documentation, including:
48 |
49 | - Fixing typos and grammatical errors.
50 | - Adding missing documentation for features.
51 | - Adding missing documentation for configuration options.
52 | - Adding demo images/gifs for features.
53 |
--------------------------------------------------------------------------------
/EXTEND_IRB.md:
--------------------------------------------------------------------------------
1 | # Extend IRB
2 |
3 | This page has been moved to the [IRB Extension Guide](https://ruby.github.io/irb/EXTEND_IRB_md.html).
4 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 |
3 | gemspec
4 |
5 | is_unix = RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i
6 | is_truffleruby = RUBY_DESCRIPTION =~ /truffleruby/
7 |
8 | if is_unix && ENV['WITH_VTERM']
9 | gem "vterm", github: "ruby/vterm-gem"
10 | gem "yamatanooroti", github: "ruby/yamatanooroti"
11 | end
12 |
13 | gem "stackprof" if is_unix && !is_truffleruby
14 |
15 | gem "reline", github: "ruby/reline" if ENV["WITH_LATEST_RELINE"] == "true"
16 | gem "rake"
17 | gem "test-unit"
18 | gem "test-unit-ruby-core"
19 |
20 | gem "rubocop"
21 |
22 | gem "tracer" if !is_truffleruby
23 | gem "debug", github: "ruby/debug", platforms: [:mri, :mswin]
24 |
25 | gem "rdoc", ">= 6.11.0"
26 |
27 | if RUBY_VERSION >= "3.0.0" && !is_truffleruby
28 | gem "repl_type_completor"
29 | end
30 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
2 |
3 | Redistribution and use in source and binary forms, with or without
4 | modification, are permitted provided that the following conditions
5 | are met:
6 | 1. Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | 2. Redistributions in binary form must reproduce the above copyright
9 | notice, this list of conditions and the following disclaimer in the
10 | documentation and/or other materials provided with the distribution.
11 |
12 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22 | SUCH DAMAGE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # IRB
2 |
3 | [](https://badge.fury.io/rb/irb)
4 | [](https://ruby.github.io/irb/)
5 | [](https://github.com/ruby/irb/actions/workflows/test.yml)
6 |
7 |
8 | IRB stands for "interactive Ruby" and is a tool to interactively execute Ruby expressions read from the standard input.
9 |
10 | The `irb` command from your shell will start the interpreter.
11 |
12 | ## Installation
13 |
14 | > [!Note]
15 | >
16 | > IRB is a default gem of Ruby so you shouldn't need to install it separately.
17 | >
18 | > But if you're using Ruby 2.6 or later and want to upgrade/install a specific version of IRB, please follow these steps.
19 |
20 | To install it with `bundler`, add this line to your application's Gemfile:
21 |
22 | ```ruby
23 | gem 'irb'
24 | ```
25 |
26 | And then execute:
27 |
28 | ```shell
29 | $ bundle
30 | ```
31 |
32 | Or install it directly with:
33 |
34 | ```shell
35 | $ gem install irb
36 | ```
37 |
38 | ## Usage
39 |
40 | > [!Note]
41 | >
42 | > We're working hard to match Pry's variety of powerful features in IRB, and you can track our progress or find contribution ideas in [this document](https://ruby.github.io/irb/COMPARED_WITH_PRY_md.html).
43 |
44 | ### The `irb` Executable
45 |
46 | You can start a fresh IRB session by typing `irb` in your terminal.
47 |
48 | In the session, you can evaluate Ruby expressions or even prototype a small Ruby script. An input is executed when it is syntactically complete.
49 |
50 | ```shell
51 | $ irb
52 | irb(main):001> 1 + 2
53 | => 3
54 | irb(main):002* class Foo
55 | irb(main):003* def foo
56 | irb(main):004* puts 1
57 | irb(main):005* end
58 | irb(main):006> end
59 | => :foo
60 | irb(main):007> Foo.new.foo
61 | 1
62 | => nil
63 | ```
64 |
65 | ### The `binding.irb` Breakpoint
66 |
67 | If you use Ruby 2.5 or later versions, you can also use `binding.irb` in your program as breakpoints.
68 |
69 | Once a `binding.irb` is evaluated, a new IRB session will be started with the surrounding context:
70 |
71 | ```shell
72 | $ ruby test.rb
73 |
74 | From: test.rb @ line 2 :
75 |
76 | 1: def greet(word)
77 | => 2: binding.irb
78 | 3: puts "Hello #{word}"
79 | 4: end
80 | 5:
81 | 6: greet("World")
82 |
83 | irb(main):001:0> word
84 | => "World"
85 | irb(main):002:0> exit
86 | Hello World
87 | ```
88 |
89 | ### Debugging
90 |
91 | You can use IRB as a debugging console with `debug.gem` with these options:
92 |
93 | - In `binding.irb`, use the `debug` command to start an `irb:rdbg` session with access to all `debug.gem` commands.
94 | - Use the `RUBY_DEBUG_IRB_CONSOLE=1` environment variable to make `debug.gem` use IRB as the debugging console.
95 |
96 | To learn more about debugging with IRB, see [Debugging with IRB](https://ruby.github.io/irb/#label-Debugging+with+IRB).
97 |
98 | ## Documentation
99 |
100 | https://ruby.github.io/irb/ provides a comprehensive guide to IRB's features and usage.
101 |
102 | ## Configuration
103 |
104 | See the [Configuration page](https://ruby.github.io/irb/Configurations_md.html) in the documentation.
105 |
106 | ## Extending IRB
107 |
108 | IRB `v1.13.0` and later versions allows users/libraries to extend its functionality through official APIs.
109 |
110 | For more information, please visit the [IRB Extension Guide](https://ruby.github.io/irb/EXTEND_IRB_md.html).
111 |
112 | ## Contributing
113 |
114 | See [CONTRIBUTING.md](./CONTRIBUTING.md) for more information.
115 |
116 | ## Releasing
117 |
118 | ```
119 | rake release
120 | gh release create vX.Y.Z --generate-notes
121 | ```
122 |
123 | ## License
124 |
125 | The gem is available as open source under the terms of the [2-Clause BSD License](https://opensource.org/licenses/BSD-2-Clause).
126 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "bundler/gem_tasks"
2 | require "rake/testtask"
3 | require "rdoc/task"
4 |
5 | Rake::TestTask.new(:test) do |t|
6 | t.libs << "test" << "test/lib"
7 | t.libs << "lib"
8 | t.ruby_opts << "-rhelper"
9 | t.test_files = FileList["test/irb/**/test_*.rb"]
10 | end
11 |
12 | # To make sure they have been correctly setup for Ruby CI.
13 | desc "Run each irb test file in isolation."
14 | task :test_in_isolation do
15 | failed = false
16 |
17 | FileList["test/irb/**/test_*.rb"].each do |test_file|
18 | ENV["TEST"] = test_file
19 | begin
20 | Rake::Task["test"].execute
21 | rescue
22 | failed = true
23 | msg = "Test '#{test_file}' failed when being executed in isolation. Please make sure 'rake test TEST=#{test_file}' passes."
24 | separation_line = '=' * msg.length
25 |
26 | puts <<~MSG
27 | #{separation_line}
28 | #{msg}
29 | #{separation_line}
30 | MSG
31 | end
32 | end
33 |
34 | fail "Some tests failed when being executed in isolation" if failed
35 | end
36 |
37 | Rake::TestTask.new(:test_yamatanooroti) do |t|
38 | t.libs << 'test' << "test/lib"
39 | t.libs << 'lib'
40 | #t.loader = :direct
41 | t.ruby_opts << "-rhelper"
42 | t.pattern = 'test/irb/yamatanooroti/test_*.rb'
43 | end
44 |
45 | task :default => :test
46 |
47 | RDoc::Task.new do |rdoc|
48 | rdoc.title = "IRB Documentation"
49 | rdoc.main = "Index.md"
50 | rdoc.rdoc_dir = "_site"
51 | rdoc.options.push("lib")
52 | end
53 |
--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | require "bundler/setup"
4 | require_relative "../lib/irb"
5 |
6 | IRB.start(__FILE__)
7 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 | IFS=$'\n\t'
4 | set -vx
5 |
6 | bundle install
7 |
--------------------------------------------------------------------------------
/doc/.document:
--------------------------------------------------------------------------------
1 | *.md
2 |
--------------------------------------------------------------------------------
/doc/COMMAND_LINE_OPTIONS.md:
--------------------------------------------------------------------------------
1 | # Index of Command-Line Options
2 |
3 | These are the IRB command-line options, with links to explanatory text:
4 |
5 | - `-d`: Set `$DEBUG` and {$VERBOSE}[rdoc-ref:IRB@Verbosity]
6 | to `true`.
7 | - `-E _ex_[:_in_]`: Set initial external (ex) and internal (in)
8 | {encodings}[rdoc-ref:IRB@Encodings] (same as `ruby -E`).
9 | - `-f`: Don't initialize from {configuration file}[rdoc-ref:IRB@Configuration+File].
10 | - `-I _dirpath_`: Specify {$LOAD_PATH directory}[rdoc-ref:IRB@Load+Modules]
11 | (same as `ruby -I`).
12 | - `-r _load-module_`: Require {load-module}[rdoc-ref:IRB@Load+Modules]
13 | (same as `ruby -r`).
14 | - `-U`: Set external and internal {encodings}[rdoc-ref:IRB@Encodings] to UTF-8.
15 | - `-w`: Suppress {warnings}[rdoc-ref:IRB@Warnings] (same as `ruby -w`).
16 | - `-W[_level_]`: Set {warning level}[rdoc-ref:IRB@Warnings];
17 | 0=silence, 1=medium, 2=verbose (same as `ruby -W`).
18 | - `--autocomplete`: Use {auto-completion}[rdoc-ref:IRB@Automatic+Completion].
19 | - `--back-trace-limit _n_`: Set a {backtrace limit}[rdoc-ref:IRB@Tracer];
20 | display at most the top `n` and bottom `n` entries.
21 | - `--colorize`: Use {color-highlighting}[rdoc-ref:IRB@Color+Highlighting]
22 | for input and output.
23 | - `--context-mode _n_`: Select method to create Binding object
24 | for new {workspace}[rdoc-ref:IRB@Commands]; `n` in range `0..4`.
25 | - `--echo`: Print ({echo}[rdoc-ref:IRB@Return-Value+Printing+-28Echoing-29])
26 | return values.
27 | - `--extra-doc-dir _dirpath_`:
28 | Add a {documentation directory}[rdoc-ref:IRB@RI+Documentation+Directories]
29 | for the documentation dialog.
30 | - `--inf-ruby-mode`: Set prompt mode to {:INF_RUBY}[rdoc-ref:IRB@Pre-Defined+Prompts]
31 | (appropriate for `inf-ruby-mode` on Emacs);
32 | suppresses --multiline and --singleline.
33 | - `--inspect`: Use method `inspect` for printing ({echoing}[rdoc-ref:IRB@Return-Value+Printing+-28Echoing-29])
34 | return values.
35 | - `--multiline`: Use the multiline editor as the {input method}[rdoc-ref:IRB@Input+Method].
36 | - `--noautocomplete`: Don't use {auto-completion}[rdoc-ref:IRB@Automatic+Completion].
37 | - `--nocolorize`: Don't use {color-highlighting}[rdoc-ref:IRB@Color+Highlighting]
38 | for input and output.
39 | - `--noecho`: Don't print ({echo}[rdoc-ref:IRB@Return-Value+Printing+-28Echoing-29])
40 | return values.
41 | - `--noecho-on-assignment`: Don't print ({echo}[rdoc-ref:IRB@Return-Value+Printing+-28Echoing-29])
42 | result on assignment.
43 | - `--noinspect`: Don't se method `inspect` for printing ({echoing}[rdoc-ref:IRB@Return-Value+Printing+-28Echoing-29])
44 | return values.
45 | - `--nomultiline`: Don't use the multiline editor as the {input method}[rdoc-ref:IRB@Input+Method].
46 | - `--noprompt`: Don't print {prompts}[rdoc-ref:IRB@Prompt+and+Return+Formats].
47 | - `--noscript`: Treat the first command-line argument as a normal
48 | {command-line argument}[rdoc-ref:IRB@Initialization+Script],
49 | and include it in `ARGV`.
50 | - `--nosingleline`: Don't use the singleline editor as the {input method}[rdoc-ref:IRB@Input+Method].
51 | - `--noverbose`: Don't print {verbose}[rdoc-ref:IRB@Verbosity] details.
52 | - `--prompt _mode_`, `--prompt-mode _mode_`:
53 | Set {prompt and return formats}[rdoc-ref:IRB@Prompt+and+Return+Formats];
54 | `mode` may be a {pre-defined prompt}[rdoc-ref:IRB@Pre-Defined+Prompts]
55 | or the name of a {custom prompt}[rdoc-ref:IRB@Custom+Prompts].
56 | - `--script`: Treat the first command-line argument as the path to an
57 | {initialization script}[rdoc-ref:IRB@Initialization+Script],
58 | and omit it from `ARGV`.
59 | - `--simple-prompt`, `--sample-book-mode`:
60 | Set prompt mode to {:SIMPLE}[rdoc-ref:IRB@Pre-Defined+Prompts].
61 | - `--singleline`: Use the singleline editor as the {input method}[rdoc-ref:IRB@Input+Method].
62 | - `--tracer`: Use {Tracer}[rdoc-ref:IRB@Tracer] to print a stack trace for each input command.
63 | - `--truncate-echo-on-assignment`: Print ({echo}[rdoc-ref:IRB@Return-Value+Printing+-28Echoing-29])
64 | truncated result on assignment.
65 | - `--verbose`Print {verbose}[rdoc-ref:IRB@Verbosity] details.
66 | - `-v`, `--version`: Print the {IRB version}[rdoc-ref:IRB@Version].
67 | - `-h`, `--help`: Print the {IRB help text}[rdoc-ref:IRB@Help].
68 | - `--`: Separate options from {arguments}[rdoc-ref:IRB@Command-Line+Arguments]
69 | on the command-line.
70 |
--------------------------------------------------------------------------------
/doc/COMPARED_WITH_PRY.md:
--------------------------------------------------------------------------------
1 | # Comparison with Pry
2 |
3 | 👋 IRB is on a mission to match feature parity with Pry. This document is here to help us track the similarities and differences between the two.
4 |
5 | Feel free to chip in and update this table - we appreciate your help!
6 |
7 | | Feature | Pry | IRB | Note |
8 | | ------------------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- |
9 | | Supported Rubies | `>= 2.0` | `>= 2.7` | |
10 | | Source code browsing | `show-source` | `show_source` | IRB's `show_source` can't display C source. See [#664](https://github.com/ruby/irb/issues/664) |
11 | | Document browsing | `ri` | `show_doc` | |
12 | | Live help system | `help` or `command_name --help` | `help` | IRB doesn't support detailed descriptions for individual commands yet |
13 | | Open methods in editors | `edit` | `edit` | |
14 | | Syntax highlighting | Yes | Yes | |
15 | | Command shell integration | Yes | No | Currently, there's no plan to support such features in IRB |
16 | | Navigation | - `cd object` to enter `object`
- `cd ..` to leave the current object
- `nesting` to list nesting levels | - `pushws object`
- `popws`
- `workspaces`
- `cd object`/`cd ..` (since [1.14.0](https://github.com/ruby/irb/releases/tag/v1.14.0)) | We plan to refine IRB's commands in the future |
17 | | Runtime invocation | `binding.pry` | `binding.irb` | |
18 | | Command system | Yes | No | Planned in #513 and #588 |
19 | | Input history | [Comprehensive support](https://github.com/pry/pry/wiki/History) | Supports retrieving previous input and the `history` command | The history command doesn't support as many flags yet, but contributions are welcome. |
20 | | Pager support | Command output and return value | Command output and return value | |
21 | | Debugger integration | With `byebug` through [pry-byebug](https://github.com/deivid-rodriguez/pry-byebug) gem | Supports [`irb:rdbg`](https://github.com/ruby/irb#debugging-with-irb) sessions | |
22 | | Rails console | Through [`pry-rails`](https://github.com/pry/pry-rails) gem | Rails' default | Rails console with IRB doesn't have commands like `show-routes` or `show-models` |
23 |
--------------------------------------------------------------------------------
/doc/EXTEND_IRB.md:
--------------------------------------------------------------------------------
1 | # Extend IRB
2 |
3 | From v1.13.0, IRB provides official APIs to extend its functionality. This feature allows libraries to
4 | customize and enhance their users' IRB sessions by adding new commands and helper methods tailored for
5 | the libraries.
6 |
7 | ## Helper Methods vs. Commands
8 |
9 | - Use a helper method if the operation is meant to return a Ruby object that interacts with the application.
10 | - For example, an `admin_user` helper method that returns `User.where(admin: true).first`, which can then be used like `login_as(admin_user)`.
11 | - Use a command if the operation fits one of the following:
12 | - A utility operation that performs non-Ruby related tasks, such as IRB's `edit` command.
13 | - Displays information, like the `show_source` command.
14 | - If the operation requires non-Ruby syntax arguments, like `ls -g pattern`.
15 |
16 | If you don't know what to pick, go with commands first. Commands are generally safer as they can handle a wider variety of inputs and use cases.
17 |
18 | ## Commands
19 |
20 | Commands are designed to complete certain tasks or display information for the user, similar to shell commands.
21 | Therefore, they are designed to accept a variety of inputs, including those that are not valid Ruby code, such
22 | as `my_cmd Foo#bar` or `my_cmd --flag foo`.
23 |
24 | ### Example
25 |
26 | ```rb
27 | require "irb/command"
28 |
29 | class Greet < IRB::Command::Base
30 | category "Greeting"
31 | description "Greets the user"
32 | help_message <<~HELP
33 | Greets the user with the given name.
34 |
35 | Usage: greet
36 | HELP
37 |
38 | # Any input after the command name will be passed as a single string.
39 | # If nothing is added after the command, an empty string will be passed.
40 | def execute(arg)
41 | puts "Hello! #{arg}"
42 | end
43 | end
44 |
45 | IRB::Command.register(:greet, Greet)
46 | ```
47 |
48 | As long as the above code is loaded before the IRB session is started, such as in a loaded library or a user's `.irbrc` file, `greet` will be accessible to the user.
49 |
50 | ```txt
51 | irb(main):001> greet
52 | Hello!
53 | => nil
54 | irb(main):002> greet Stan
55 | Hello! Stan
56 | => nil
57 | ```
58 |
59 | And because the `Greet` command introduces a new category, `Greeting`, a new help message category will be created:
60 |
61 | ```txt
62 | Help
63 | help List all available commands. Use `help ` to get information about a specific command.
64 |
65 | Greeting
66 | greet Greets the user
67 |
68 | IRB
69 | context Displays current configuration.
70 | ...
71 | ```
72 |
73 | If the optional `help_message` attribute is specified, `help greet` will also display it:
74 |
75 | ```txt
76 | irb(main):001> help greet
77 | Greets the user with the given name.
78 |
79 | Usage: greet
80 | ```
81 |
82 | ## Helper methods
83 |
84 | Helper methods are designed to be used as Ruby methods, such as `my_helper(arg, kwarg: val).foo`.
85 |
86 | The main use case of helper methods is to provide shortcuts for users, providing quick and easy access to
87 | frequently used operations or components within the IRB session. For example, a helper method might simplify
88 | the process of fetching and displaying specific configuration settings or data structures that would otherwise
89 | require multiple steps to access.
90 |
91 | ### Example
92 |
93 | ```rb
94 | # This only loads the minimum components required to define and register a helper method.
95 | # It does not load the entire IRB, nor does it initialize it.
96 | require "irb/helper_method"
97 |
98 | class MyHelper < IRB::HelperMethod::Base
99 | description "This is a test helper"
100 |
101 | def execute(arg, kwarg:)
102 | "arg: #{arg}, kwarg: #{kwarg}"
103 | end
104 | end
105 |
106 | IRB::HelperMethod.register(:my_helper, MyHelper)
107 | ```
108 |
109 | As long as the above code is loaded before the IRB session is started, such as in a loaded library or a user's `.irbrc` file, `my_helper` will be accessible to the user.
110 |
111 | ```txt
112 | irb(main):001> my_helper("foo", kwarg: "bar").upcase
113 | => "ARG: FOO, KWARG: BAR"
114 | ```
115 |
116 | The registered helper methods will also be listed in the help message's `Helper methods` section:
117 |
118 | ```txt
119 | Helper methods
120 | conf Returns the current context.
121 | my_helper This is a test helper
122 | ```
123 |
--------------------------------------------------------------------------------
/doc/irb/irb-tools.rd.ja:
--------------------------------------------------------------------------------
1 | irb関連おまけコマンドとライブラリ
2 | $Release Version: 0.7.1 $
3 | $Revision$
4 | by Keiju ISHITSUKA(Nihon Rational Co.,Ltd.)
5 |
6 | =begin
7 |
8 | :コマンド:
9 | * rtags -- ruby tags command
10 |
11 | :関数ライブラリ:
12 | * xmp -- irb version of gotoken xmp-function
13 |
14 | :クラスライブラリ:
15 | * frame.rb -- frame tracer
16 | * completion.rb -- irb completor
17 |
18 | = rtags
19 |
20 | rtagsはemacs及びvi用の, TAGファイルをつくるコマンドです.
21 |
22 | == 使い方
23 |
24 | rtags [-vi] file....
25 |
26 | カレントディレクトリにemacs用のTAGSファイルができます. -viオプションを
27 | つけた時にはvi用のtagsファイルを作成します.
28 |
29 | emacsの場合, 通常のetags.elがそのまま使えます. 検索可能なのは,
30 |
31 | * クラス
32 | * メソッド
33 | * 特異メソッド
34 | * alias
35 | * attrで宣言されたアクセサ(パラメータがシンボルか文字列リテラルに限る)
36 | * attr_XXXで宣言されたアクセサ(パラメータがシンボルか文字列リテラルに限る)
37 |
38 | です.
39 |
40 | Cなどで使っているのと違うのは, コンプリーションに関する部分で,
41 |
42 | 関数名は,
43 |
44 | 関数名(
45 |
46 | クラスは,
47 |
48 | ::クラス名::....::クラス名
49 |
50 | メソッドは,
51 |
52 | ::クラス名::....::クラス名#メソッド名
53 |
54 | 特異メソッド(クラスメソッド)は
55 |
56 | ::クラス名::....::クラス名.メソッド名
57 |
58 | でコンプリーションを行なうところです.
59 |
60 | = xmp.rb
61 |
62 | ごとけんxmpの上位互換バージョンです. ただ, 非常に重いのでごとけんxmpで
63 | は対応できない時に, 使用すると良いでしょう.
64 |
65 | == 使い方
66 |
67 | === 関数として使う.
68 |
69 | require "irb/xmp"
70 | xmp <1
77 | foo
78 | ==>1
79 |
80 | === XMPインスタンスを用いる.
81 |
82 | この場合は, XMPがコンテキスト情報を持つので, 変数の値などを保持してい
83 | ます.
84 |
85 | require "irb/xmp"
86 | xmp = XMP.new
87 | xmp.puts <1
97 | foo
98 | ==>1
99 | foo
100 | ==>1
101 |
102 | == コンテキストに関して
103 |
104 | XMPメソッド群のコンテキストは, 呼び出す前のコンテキストで評価されます.
105 | 明示的にコンテキストを指定するとそのコンテキストで評価します.
106 |
107 | 例:
108 |
109 | xmp "foo", an_binding
110 |
111 | :注:
112 | マルチスレッドには対応していません.
113 |
114 | = frame.rb
115 | 現在実行中のフレーム情報を取り扱うためのクラスです.
116 |
117 | * IRB::Frame.top(n = 0)
118 | 上からn番目のコンテキストを取り出します. nは0が最上位になります.
119 | * IRB::Frame.bottom(n = 0)
120 | 下からn番目のコンテキストを取り出します. nは0が最下位になります.
121 | * IRB::Frame.sender
122 | センダになっているオブジェクトを取り出します. センダとは, そのメソッ
123 | ドを呼び出した側のselfのことです.
124 |
125 | :注:
126 | set_trace_funcを用いてRubyの実行をトレースしています. マルチスレッドに
127 | は対応していません.
128 |
129 | = completion.rb
130 | irbのcompletion機能を提供するものです.
131 |
132 | == 使い方
133 |
134 | % irb -r irb/completion
135 |
136 | とするか, ~/.irbrc 中に
137 |
138 | require "irb/completion"
139 |
140 | を入れてください. irb実行中に require "irb/completion" してもよいです.
141 |
142 | irb実行中に (TAB) を押すとコンプレーションします.
143 |
144 | トップレベルで(TAB)を押すとすべての構文要素, クラス, メソッドの候補がで
145 | ます. 候補が唯一ならば完全に補完します.
146 |
147 | irb(main):001:0> in
148 | in inspect instance_eval
149 | include install_alias_method instance_of?
150 | initialize install_aliases instance_variables
151 | irb(main):001:0> inspect
152 | "main"
153 | irb(main):002:0> foo = Object.new
154 | #
155 |
156 | ((|変数名.|))の後に(TAB)を押すと, そのオブジェクトのメソッド一覧がでま
157 | す.
158 |
159 | irb(main):003:0> foo.
160 | foo.== foo.frozen? foo.protected_methods
161 | foo.=== foo.hash foo.public_methods
162 | foo.=~ foo.id foo.respond_to?
163 | foo.__id__ foo.inspect foo.send
164 | foo.__send__ foo.instance_eval foo.singleton_methods
165 | foo.class foo.instance_of? foo.taint
166 | foo.clone foo.instance_variables foo.tainted?
167 | foo.display foo.is_a? foo.to_a
168 | foo.dup foo.kind_of? foo.to_s
169 | foo.eql? foo.method foo.type
170 | foo.equal? foo.methods foo.untaint
171 | foo.extend foo.nil?
172 | foo.freeze foo.private_methods
173 |
174 | =end
175 |
176 | % Begin Emacs Environment
177 | % Local Variables:
178 | % mode: text
179 | % comment-column: 0
180 | % comment-start: "%"
181 | % comment-end: "\n"
182 | % End:
183 | %
184 |
185 |
--------------------------------------------------------------------------------
/exe/irb:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | #
3 | # irb.rb - interactive ruby
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | require "irb"
8 |
9 | IRB.start(__FILE__)
10 |
--------------------------------------------------------------------------------
/irb.gemspec:
--------------------------------------------------------------------------------
1 | begin
2 | require_relative "lib/irb/version"
3 | rescue LoadError
4 | # for Ruby core repository
5 | require_relative "version"
6 | end
7 |
8 | Gem::Specification.new do |spec|
9 | spec.name = "irb"
10 | spec.version = IRB::VERSION
11 | spec.authors = ["aycabta", "Keiju ISHITSUKA"]
12 | spec.email = ["aycabta@gmail.com", "keiju@ruby-lang.org"]
13 |
14 | spec.summary = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).}
15 | spec.description = %q{Interactive Ruby command-line tool for REPL (Read Eval Print Loop).}
16 | spec.homepage = "https://github.com/ruby/irb"
17 | spec.licenses = ["Ruby", "BSD-2-Clause"]
18 |
19 | spec.metadata["homepage_uri"] = spec.homepage
20 | spec.metadata["source_code_uri"] = spec.homepage
21 | spec.metadata["documentation_uri"] = "https://ruby.github.io/irb/"
22 | spec.metadata["changelog_uri"] = "#{spec.homepage}/releases"
23 |
24 | spec.files = [
25 | "Gemfile",
26 | "LICENSE.txt",
27 | "README.md",
28 | "Rakefile",
29 | "bin/console",
30 | "bin/setup",
31 | "doc/irb/irb-tools.rd.ja",
32 | "doc/irb/irb.rd.ja",
33 | "exe/irb",
34 | "irb.gemspec",
35 | "man/irb.1",
36 | ] + Dir.chdir(File.expand_path('..', __FILE__)) do
37 | Dir.glob("lib/**/*").map {|f| f unless File.directory?(f) }.compact
38 | end
39 | spec.bindir = "exe"
40 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
41 | spec.require_paths = ["lib"]
42 |
43 | spec.required_ruby_version = Gem::Requirement.new(">= 2.7")
44 |
45 | spec.add_dependency "reline", ">= 0.4.2"
46 | spec.add_dependency "rdoc", ">= 4.0.0"
47 | spec.add_dependency "pp", ">= 0.6.0"
48 | end
49 |
--------------------------------------------------------------------------------
/lib/.document:
--------------------------------------------------------------------------------
1 | irb.rb
2 | irb/context.rb
3 | irb/command/base.rb
4 |
--------------------------------------------------------------------------------
/lib/irb/.document:
--------------------------------------------------------------------------------
1 | **/*.rb
2 |
--------------------------------------------------------------------------------
/lib/irb/cmd/nop.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # This file is just a placeholder for backward-compatibility.
4 | # Please require 'irb' and inherit your command from `IRB::Command::Base` instead.
5 |
--------------------------------------------------------------------------------
/lib/irb/color_printer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require 'pp'
3 | require_relative 'color'
4 |
5 | module IRB
6 | class ColorPrinter < ::PP
7 | class << self
8 | def pp(obj, out = $>, width = screen_width, colorize: true)
9 | q = ColorPrinter.new(out, width, colorize: colorize)
10 | q.guard_inspect_key {q.pp obj}
11 | q.flush
12 | out << "\n"
13 | end
14 |
15 | private
16 |
17 | def screen_width
18 | Reline.get_screen_size.last
19 | rescue Errno::EINVAL # in `winsize': Invalid argument -
20 | 79
21 | end
22 | end
23 |
24 | def initialize(out, width, colorize: true)
25 | @colorize = colorize
26 |
27 | super(out, width)
28 | end
29 |
30 | def pp(obj)
31 | if String === obj
32 | # Avoid calling Ruby 2.4+ String#pretty_print that splits a string by "\n"
33 | text(obj.inspect)
34 | else
35 | super
36 | end
37 | end
38 |
39 | def text(str, width = nil)
40 | unless str.is_a?(String)
41 | str = str.inspect
42 | end
43 | width ||= str.length
44 |
45 | case str
46 | when ''
47 | when ',', '=>', '[', ']', '{', '}', '..', '...', /\A@\w+\z/
48 | super(str, width)
49 | when /\A#, '=', '>'
50 | super(@colorize ? Color.colorize(str, [:GREEN]) : str, width)
51 | else
52 | super(@colorize ? Color.colorize_code(str, ignore_error: true) : str, width)
53 | end
54 | end
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/lib/irb/command.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # irb/command.rb - irb command
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | require_relative "command/base"
8 |
9 | module IRB # :nodoc:
10 | module Command
11 | @commands = {}
12 |
13 | class << self
14 | attr_reader :commands
15 |
16 | # Registers a command with the given name.
17 | # Aliasing is intentionally not supported at the moment.
18 | def register(name, command_class)
19 | @commands[name.to_sym] = [command_class, []]
20 | end
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/lib/irb/command/backtrace.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "debug"
4 |
5 | module IRB
6 | # :stopdoc:
7 |
8 | module Command
9 | class Backtrace < DebugCommand
10 | def execute(arg)
11 | execute_debug_command(pre_cmds: "backtrace #{arg}")
12 | end
13 | end
14 | end
15 |
16 | # :startdoc:
17 | end
18 |
--------------------------------------------------------------------------------
/lib/irb/command/base.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # nop.rb -
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | module IRB
8 | module Command
9 | class CommandArgumentError < StandardError; end # :nodoc:
10 |
11 | class << self
12 | def extract_ruby_args(*args, **kwargs) # :nodoc:
13 | throw :EXTRACT_RUBY_ARGS, [args, kwargs]
14 | end
15 | end
16 |
17 | class Base
18 | class << self
19 | def category(category = nil)
20 | @category = category if category
21 | @category || "No category"
22 | end
23 |
24 | def description(description = nil)
25 | @description = description if description
26 | @description || "No description provided."
27 | end
28 |
29 | def help_message(help_message = nil)
30 | @help_message = help_message if help_message
31 | @help_message
32 | end
33 |
34 | def execute(irb_context, arg)
35 | new(irb_context).execute(arg)
36 | rescue CommandArgumentError => e
37 | puts e.message
38 | end
39 |
40 | private
41 |
42 | def highlight(text)
43 | Color.colorize(text, [:BOLD, :BLUE])
44 | end
45 | end
46 |
47 | def initialize(irb_context)
48 | @irb_context = irb_context
49 | end
50 |
51 | attr_reader :irb_context
52 |
53 | def execute(arg)
54 | #nop
55 | end
56 | end
57 |
58 | Nop = Base # :nodoc:
59 | end
60 | end
61 |
--------------------------------------------------------------------------------
/lib/irb/command/break.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "debug"
4 |
5 | module IRB
6 | # :stopdoc:
7 |
8 | module Command
9 | class Break < DebugCommand
10 | def execute(arg)
11 | execute_debug_command(pre_cmds: "break #{arg}")
12 | end
13 | end
14 | end
15 |
16 | # :startdoc:
17 | end
18 |
--------------------------------------------------------------------------------
/lib/irb/command/catch.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "debug"
4 |
5 | module IRB
6 | # :stopdoc:
7 |
8 | module Command
9 | class Catch < DebugCommand
10 | def execute(arg)
11 | execute_debug_command(pre_cmds: "catch #{arg}")
12 | end
13 | end
14 | end
15 |
16 | # :startdoc:
17 | end
18 |
--------------------------------------------------------------------------------
/lib/irb/command/cd.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module IRB
4 | module Command
5 | class CD < Base
6 | category "Workspace"
7 | description "Move into the given object or leave the current context."
8 |
9 | help_message(<<~HELP)
10 | Usage: cd ([target]|..)
11 |
12 | IRB uses a stack of workspaces to keep track of context(s), with `pushws` and `popws` commands to manipulate the stack.
13 | The `cd` command is an attempt to simplify the operation and will be subject to change.
14 |
15 | When given:
16 | - an object, cd will use that object as the new context by pushing it onto the workspace stack.
17 | - "..", cd will leave the current context by popping the top workspace off the stack.
18 | - no arguments, cd will move to the top workspace on the stack by popping off all workspaces.
19 |
20 | Examples:
21 |
22 | cd Foo
23 | cd Foo.new
24 | cd @ivar
25 | cd ..
26 | cd
27 | HELP
28 |
29 | def execute(arg)
30 | case arg
31 | when ".."
32 | irb_context.pop_workspace
33 | when ""
34 | # TODO: decide what workspace commands should be kept, and underlying APIs should look like,
35 | # and perhaps add a new API to clear the workspace stack.
36 | prev_workspace = irb_context.pop_workspace
37 | while prev_workspace
38 | prev_workspace = irb_context.pop_workspace
39 | end
40 | else
41 | begin
42 | obj = eval(arg, irb_context.workspace.binding)
43 | irb_context.push_workspace(obj)
44 | rescue StandardError => e
45 | warn "Error: #{e}"
46 | end
47 | end
48 | end
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/lib/irb/command/chws.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # change-ws.rb -
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 | require_relative "../ext/change-ws"
7 |
8 | module IRB
9 | # :stopdoc:
10 |
11 | module Command
12 |
13 | class CurrentWorkingWorkspace < Base
14 | category "Workspace"
15 | description "Show the current workspace."
16 |
17 | def execute(_arg)
18 | puts "Current workspace: #{irb_context.main}"
19 | end
20 | end
21 |
22 | class ChangeWorkspace < Base
23 | category "Workspace"
24 | description "Change the current workspace to an object."
25 |
26 | def execute(arg)
27 | if arg.empty?
28 | irb_context.change_workspace
29 | else
30 | obj = eval(arg, irb_context.workspace.binding)
31 | irb_context.change_workspace(obj)
32 | end
33 |
34 | puts "Current workspace: #{irb_context.main}"
35 | end
36 | end
37 | end
38 |
39 | # :startdoc:
40 | end
41 |
--------------------------------------------------------------------------------
/lib/irb/command/context.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module IRB
4 | module Command
5 | class Context < Base
6 | category "IRB"
7 | description "Displays current configuration."
8 |
9 | def execute(_arg)
10 | # This command just displays the configuration.
11 | # Modifying the configuration is achieved by sending a message to IRB.conf.
12 | Pager.page_content(IRB.CurrentContext.inspect)
13 | end
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/lib/irb/command/continue.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "debug"
4 |
5 | module IRB
6 | # :stopdoc:
7 |
8 | module Command
9 | class Continue < DebugCommand
10 | def execute(arg)
11 | execute_debug_command(do_cmds: "continue #{arg}")
12 | end
13 | end
14 | end
15 |
16 | # :startdoc:
17 | end
18 |
--------------------------------------------------------------------------------
/lib/irb/command/copy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module IRB
4 | module Command
5 | class Copy < Base
6 | category "Misc"
7 | description "Copy expression output to clipboard"
8 |
9 | help_message(<<~HELP)
10 | Usage: copy ([expression])
11 |
12 | When given:
13 | - an expression, copy the inspect result of the expression to the clipboard.
14 | - no arguments, copy the last evaluated result (`_`) to the clipboard.
15 |
16 | Examples:
17 |
18 | copy Foo.new
19 | copy User.all.to_a
20 | copy
21 | HELP
22 |
23 | def execute(arg)
24 | # Copy last value if no expression was supplied
25 | arg = '_' if arg.to_s.strip.empty?
26 |
27 | value = irb_context.workspace.binding.eval(arg)
28 | output = irb_context.inspect_method.inspect_value(value, +'', colorize: false).chomp
29 |
30 | if clipboard_available?
31 | copy_to_clipboard(output)
32 | else
33 | warn "System clipboard not found"
34 | end
35 | rescue StandardError => e
36 | warn "Error: #{e}"
37 | end
38 |
39 | private
40 |
41 | def copy_to_clipboard(text)
42 | IO.popen(clipboard_program, 'w') do |io|
43 | io.write(text)
44 | end
45 |
46 | raise IOError.new("Copying to clipboard failed") unless $? == 0
47 |
48 | puts "Copied to system clipboard"
49 | rescue Errno::ENOENT => e
50 | warn e.message
51 | warn "Is IRB.conf[:COPY_COMMAND] set to a bad value?"
52 | end
53 |
54 | def clipboard_program
55 | @clipboard_program ||= if IRB.conf[:COPY_COMMAND]
56 | IRB.conf[:COPY_COMMAND]
57 | elsif executable?("pbcopy")
58 | "pbcopy"
59 | elsif executable?("xclip")
60 | "xclip -selection clipboard"
61 | end
62 | end
63 |
64 | def executable?(command)
65 | system("which #{command} > /dev/null 2>&1")
66 | end
67 |
68 | def clipboard_available?
69 | !!clipboard_program
70 | end
71 | end
72 | end
73 | end
74 |
--------------------------------------------------------------------------------
/lib/irb/command/debug.rb:
--------------------------------------------------------------------------------
1 | require_relative "../debug"
2 |
3 | module IRB
4 | # :stopdoc:
5 |
6 | module Command
7 | class Debug < Base
8 | category "Debugging"
9 | description "Start the debugger of debug.gem."
10 |
11 | def execute(_arg)
12 | execute_debug_command
13 | end
14 |
15 | def execute_debug_command(pre_cmds: nil, do_cmds: nil)
16 | pre_cmds = pre_cmds&.rstrip
17 | do_cmds = do_cmds&.rstrip
18 |
19 | if irb_context.with_debugger
20 | # If IRB is already running with a debug session, throw the command and IRB.debug_readline will pass it to the debugger.
21 | if cmd = pre_cmds || do_cmds
22 | throw :IRB_EXIT, cmd
23 | else
24 | puts "IRB is already running with a debug session."
25 | return
26 | end
27 | else
28 | # If IRB is not running with a debug session yet, then:
29 | # 1. Check if the debugging command is run from a `binding.irb` call.
30 | # 2. If so, try setting up the debug gem.
31 | # 3. Insert a debug breakpoint at `Irb#debug_break` with the intended command.
32 | # 4. Exit the current Irb#run call via `throw :IRB_EXIT`.
33 | # 5. `Irb#debug_break` will be called and trigger the breakpoint, which will run the intended command.
34 | unless irb_context.from_binding?
35 | puts "Debugging commands are only available when IRB is started with binding.irb"
36 | return
37 | end
38 |
39 | if IRB.respond_to?(:JobManager)
40 | warn "Can't start the debugger when IRB is running in a multi-IRB session."
41 | return
42 | end
43 |
44 | unless IRB::Debug.setup(irb_context.irb)
45 | puts <<~MSG
46 | You need to install the debug gem before using this command.
47 | If you use `bundle exec`, please add `gem "debug"` into your Gemfile.
48 | MSG
49 | return
50 | end
51 |
52 | IRB::Debug.insert_debug_break(pre_cmds: pre_cmds, do_cmds: do_cmds)
53 |
54 | # exit current Irb#run call
55 | throw :IRB_EXIT
56 | end
57 | end
58 | end
59 |
60 | class DebugCommand < Debug
61 | class << self
62 | def category
63 | "Debugging"
64 | end
65 |
66 | def description
67 | command_name = self.name.split("::").last.downcase
68 | "Start the debugger of debug.gem and run its `#{command_name}` command."
69 | end
70 | end
71 | end
72 | end
73 | end
74 |
--------------------------------------------------------------------------------
/lib/irb/command/delete.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "debug"
4 |
5 | module IRB
6 | # :stopdoc:
7 |
8 | module Command
9 | class Delete < DebugCommand
10 | def execute(arg)
11 | execute_debug_command(pre_cmds: "delete #{arg}")
12 | end
13 | end
14 | end
15 |
16 | # :startdoc:
17 | end
18 |
--------------------------------------------------------------------------------
/lib/irb/command/disable_irb.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module IRB
4 | # :stopdoc:
5 |
6 | module Command
7 | class DisableIrb < Base
8 | category "IRB"
9 | description "Disable binding.irb."
10 |
11 | def execute(*)
12 | ::Binding.define_method(:irb) {}
13 | IRB.irb_exit
14 | end
15 | end
16 | end
17 |
18 | # :startdoc:
19 | end
20 |
--------------------------------------------------------------------------------
/lib/irb/command/edit.rb:
--------------------------------------------------------------------------------
1 | require 'shellwords'
2 |
3 | require_relative "../color"
4 | require_relative "../source_finder"
5 |
6 | module IRB
7 | # :stopdoc:
8 |
9 | module Command
10 | class Edit < Base
11 | include RubyArgsExtractor
12 |
13 | category "Misc"
14 | description 'Open a file or source location.'
15 | help_message <<~HELP_MESSAGE
16 | Usage: edit [FILE or constant or method signature]
17 |
18 | Open a file in the editor specified in #{highlight('ENV["VISUAL"]')} or #{highlight('ENV["EDITOR"]')}
19 |
20 | - If no arguments are provided, IRB will attempt to open the file the current context was defined in.
21 | - If FILE is provided, IRB will open the file.
22 | - If a constant or method signature is provided, IRB will attempt to locate the source file and open it.
23 |
24 | Examples:
25 |
26 | edit
27 | edit foo.rb
28 | edit Foo
29 | edit Foo#bar
30 | HELP_MESSAGE
31 |
32 | def execute(arg)
33 | # Accept string literal for backward compatibility
34 | path = unwrap_string_literal(arg)
35 |
36 | if path.nil?
37 | path = @irb_context.irb_path
38 | elsif !File.exist?(path)
39 | source = SourceFinder.new(@irb_context).find_source(path)
40 |
41 | if source&.file_exist? && !source.binary_file?
42 | path = source.file
43 | end
44 | end
45 |
46 | unless File.exist?(path)
47 | puts "Can not find file: #{path}"
48 | return
49 | end
50 |
51 | if editor = (ENV['VISUAL'] || ENV['EDITOR'])
52 | puts "command: '#{editor}'"
53 | puts " path: #{path}"
54 | system(*Shellwords.split(editor), path)
55 | else
56 | puts "Can not find editor setting: ENV['VISUAL'] or ENV['EDITOR']"
57 | end
58 | end
59 | end
60 | end
61 |
62 | # :startdoc:
63 | end
64 |
--------------------------------------------------------------------------------
/lib/irb/command/exit.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module IRB
4 | # :stopdoc:
5 |
6 | module Command
7 | class Exit < Base
8 | category "IRB"
9 | description "Exit the current irb session."
10 |
11 | def execute(_arg)
12 | IRB.irb_exit
13 | end
14 | end
15 | end
16 |
17 | # :startdoc:
18 | end
19 |
--------------------------------------------------------------------------------
/lib/irb/command/finish.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "debug"
4 |
5 | module IRB
6 | # :stopdoc:
7 |
8 | module Command
9 | class Finish < DebugCommand
10 | def execute(arg)
11 | execute_debug_command(do_cmds: "finish #{arg}")
12 | end
13 | end
14 | end
15 |
16 | # :startdoc:
17 | end
18 |
--------------------------------------------------------------------------------
/lib/irb/command/force_exit.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module IRB
4 | # :stopdoc:
5 |
6 | module Command
7 | class ForceExit < Base
8 | category "IRB"
9 | description "Exit the current process."
10 |
11 | def execute(_arg)
12 | throw :IRB_EXIT, true
13 | end
14 | end
15 | end
16 |
17 | # :startdoc:
18 | end
19 |
--------------------------------------------------------------------------------
/lib/irb/command/help.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module IRB
4 | module Command
5 | class Help < Base
6 | category "Help"
7 | description "List all available commands. Use `help ` to get information about a specific command."
8 |
9 | def execute(command_name)
10 | content =
11 | if command_name.empty?
12 | help_message
13 | else
14 | if command_class = Command.load_command(command_name)
15 | command_class.help_message || command_class.description
16 | else
17 | "Can't find command `#{command_name}`. Please check the command name and try again.\n\n"
18 | end
19 | end
20 | Pager.page_content(content)
21 | end
22 |
23 | private
24 |
25 | def help_message
26 | commands_info = IRB::Command.all_commands_info
27 | helper_methods_info = IRB::HelperMethod.all_helper_methods_info
28 | commands_grouped_by_categories = commands_info.group_by { |cmd| cmd[:category] }
29 | commands_grouped_by_categories["Helper methods"] = helper_methods_info
30 |
31 | if irb_context.with_debugger
32 | # Remove the original "Debugging" category
33 | commands_grouped_by_categories.delete("Debugging")
34 | end
35 |
36 | longest_cmd_name_length = commands_info.map { |c| c[:display_name].length }.max
37 |
38 | output = StringIO.new
39 |
40 | help_cmds = commands_grouped_by_categories.delete("Help")
41 | no_category_cmds = commands_grouped_by_categories.delete("No category")
42 | aliases = irb_context.instance_variable_get(:@command_aliases).map do |alias_name, target|
43 | { display_name: alias_name, description: "Alias for `#{target}`" }
44 | end
45 |
46 | # Display help commands first
47 | add_category_to_output("Help", help_cmds, output, longest_cmd_name_length)
48 |
49 | # Display the rest of the commands grouped by categories
50 | commands_grouped_by_categories.each do |category, cmds|
51 | add_category_to_output(category, cmds, output, longest_cmd_name_length)
52 | end
53 |
54 | # Display commands without a category
55 | if no_category_cmds
56 | add_category_to_output("No category", no_category_cmds, output, longest_cmd_name_length)
57 | end
58 |
59 | # Display aliases
60 | add_category_to_output("Aliases", aliases, output, longest_cmd_name_length)
61 |
62 | # Append the debugger help at the end
63 | if irb_context.with_debugger
64 | # Add "Debugging (from debug.gem)" category as title
65 | add_category_to_output("Debugging (from debug.gem)", [], output, longest_cmd_name_length)
66 | output.puts DEBUGGER__.help
67 | end
68 |
69 | output.string
70 | end
71 |
72 | def add_category_to_output(category, cmds, output, longest_cmd_name_length)
73 | output.puts Color.colorize(category, [:BOLD])
74 |
75 | cmds.each do |cmd|
76 | output.puts " #{cmd[:display_name].to_s.ljust(longest_cmd_name_length)} #{cmd[:description]}"
77 | end
78 |
79 | output.puts
80 | end
81 | end
82 | end
83 | end
84 |
--------------------------------------------------------------------------------
/lib/irb/command/history.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "stringio"
4 |
5 | require_relative "../pager"
6 |
7 | module IRB
8 | # :stopdoc:
9 |
10 | module Command
11 | class History < Base
12 | category "IRB"
13 | description "Shows the input history. `-g [query]` or `-G [query]` allows you to filter the output."
14 |
15 | def execute(arg)
16 |
17 | if (match = arg&.match(/(-g|-G)\s+(?.+)\s*\z/))
18 | grep = Regexp.new(match[:grep])
19 | end
20 |
21 | formatted_inputs = irb_context.io.class::HISTORY.each_with_index.reverse_each.filter_map do |input, index|
22 | next if grep && !input.match?(grep)
23 |
24 | header = "#{index}: "
25 |
26 | first_line, *other_lines = input.split("\n")
27 | first_line = "#{header}#{first_line}"
28 |
29 | truncated_lines = other_lines.slice!(1..) # Show 1 additional line (2 total)
30 | other_lines << "..." if truncated_lines&.any?
31 |
32 | other_lines.map! do |line|
33 | " " * header.length + line
34 | end
35 |
36 | [first_line, *other_lines].join("\n") + "\n"
37 | end
38 |
39 | Pager.page_content(formatted_inputs.join)
40 | end
41 | end
42 | end
43 |
44 | # :startdoc:
45 | end
46 |
--------------------------------------------------------------------------------
/lib/irb/command/info.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "debug"
4 |
5 | module IRB
6 | # :stopdoc:
7 |
8 | module Command
9 | class Info < DebugCommand
10 | def execute(arg)
11 | execute_debug_command(pre_cmds: "info #{arg}")
12 | end
13 | end
14 | end
15 |
16 | # :startdoc:
17 | end
18 |
--------------------------------------------------------------------------------
/lib/irb/command/internal_helpers.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module IRB
4 | module Command
5 | # Internal use only, for default command's backward compatibility.
6 | module RubyArgsExtractor # :nodoc:
7 | def unwrap_string_literal(str)
8 | return if str.empty?
9 |
10 | sexp = Ripper.sexp(str)
11 | if sexp && sexp.size == 2 && sexp.last&.first&.first == :string_literal
12 | @irb_context.workspace.binding.eval(str).to_s
13 | else
14 | str
15 | end
16 | end
17 |
18 | def ruby_args(arg)
19 | # Use throw and catch to handle arg that includes `;`
20 | # For example: "1, kw: (2; 3); 4" will be parsed to [[1], { kw: 3 }]
21 | catch(:EXTRACT_RUBY_ARGS) do
22 | @irb_context.workspace.binding.eval "::IRB::Command.extract_ruby_args #{arg}"
23 | end || [[], {}]
24 | end
25 | end
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/lib/irb/command/irb_info.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module IRB
4 | # :stopdoc:
5 |
6 | module Command
7 | class IrbInfo < Base
8 | category "IRB"
9 | description "Show information about IRB."
10 |
11 | def execute(_arg)
12 | str = "Ruby version: #{RUBY_VERSION}\n"
13 | str += "IRB version: #{IRB.version}\n"
14 | str += "InputMethod: #{IRB.CurrentContext.io.inspect}\n"
15 | str += "Completion: #{IRB.CurrentContext.io.respond_to?(:completion_info) ? IRB.CurrentContext.io.completion_info : 'off'}\n"
16 | rc_files = IRB.irbrc_files
17 | str += ".irbrc paths: #{rc_files.join(", ")}\n" if rc_files.any?
18 | str += "RUBY_PLATFORM: #{RUBY_PLATFORM}\n"
19 | str += "LANG env: #{ENV["LANG"]}\n" if ENV["LANG"] && !ENV["LANG"].empty?
20 | str += "LC_ALL env: #{ENV["LC_ALL"]}\n" if ENV["LC_ALL"] && !ENV["LC_ALL"].empty?
21 | str += "East Asian Ambiguous Width: #{Reline.ambiguous_width.inspect}\n"
22 | if RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/
23 | codepage = `chcp`.b.sub(/.*: (\d+)\n/, '\1')
24 | str += "Code page: #{codepage}\n"
25 | end
26 | puts str
27 | nil
28 | end
29 | end
30 | end
31 |
32 | # :startdoc:
33 | end
34 |
--------------------------------------------------------------------------------
/lib/irb/command/load.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # load.rb -
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 | require_relative "../ext/loader"
7 |
8 | module IRB
9 | # :stopdoc:
10 |
11 | module Command
12 | class LoaderCommand < Base
13 | include RubyArgsExtractor
14 | include IrbLoader
15 |
16 | def raise_cmd_argument_error
17 | raise CommandArgumentError.new("Please specify the file name.")
18 | end
19 | end
20 |
21 | class Load < LoaderCommand
22 | category "IRB"
23 | description "Load a Ruby file."
24 |
25 | def execute(arg)
26 | args, kwargs = ruby_args(arg)
27 | execute_internal(*args, **kwargs)
28 | end
29 |
30 | def execute_internal(file_name = nil, priv = nil)
31 | raise_cmd_argument_error unless file_name
32 | irb_load(file_name, priv)
33 | end
34 | end
35 |
36 | class Require < LoaderCommand
37 | category "IRB"
38 | description "Require a Ruby file."
39 |
40 | def execute(arg)
41 | args, kwargs = ruby_args(arg)
42 | execute_internal(*args, **kwargs)
43 | end
44 |
45 | def execute_internal(file_name = nil)
46 | raise_cmd_argument_error unless file_name
47 |
48 | rex = Regexp.new("#{Regexp.quote(file_name)}(\.o|\.rb)?")
49 | return false if $".find{|f| f =~ rex}
50 |
51 | case file_name
52 | when /\.rb$/
53 | begin
54 | if irb_load(file_name)
55 | $".push file_name
56 | return true
57 | end
58 | rescue LoadError
59 | end
60 | when /\.(so|o|sl)$/
61 | return ruby_require(file_name)
62 | end
63 |
64 | begin
65 | irb_load(f = file_name + ".rb")
66 | $".push f
67 | return true
68 | rescue LoadError
69 | return ruby_require(file_name)
70 | end
71 | end
72 | end
73 |
74 | class Source < LoaderCommand
75 | category "IRB"
76 | description "Loads a given file in the current session."
77 |
78 | def execute(arg)
79 | args, kwargs = ruby_args(arg)
80 | execute_internal(*args, **kwargs)
81 | end
82 |
83 | def execute_internal(file_name = nil)
84 | raise_cmd_argument_error unless file_name
85 |
86 | source_file(file_name)
87 | end
88 | end
89 | end
90 | # :startdoc:
91 | end
92 |
--------------------------------------------------------------------------------
/lib/irb/command/ls.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "reline"
4 | require "stringio"
5 |
6 | require_relative "../pager"
7 | require_relative "../color"
8 |
9 | module IRB
10 | # :stopdoc:
11 |
12 | module Command
13 | class Ls < Base
14 | class EvaluationError < StandardError; end
15 |
16 | category "Context"
17 | description "Show methods, constants, and variables."
18 |
19 | help_message <<~HELP_MESSAGE
20 | Usage: ls [obj] [-g [query]]
21 |
22 | -g [query] Filter the output with a query.
23 | HELP_MESSAGE
24 |
25 | def evaluate(code)
26 | @irb_context.workspace.binding.eval(code)
27 | rescue Exception => e
28 | puts "#{e.class}: #{e.message}"
29 | raise EvaluationError
30 | end
31 |
32 | def execute(arg)
33 | if match = arg.match(/\A(?.+\s|)(-g|-G)\s+(?.+)$/)
34 | target = match[:target]
35 | grep = Regexp.new(match[:grep])
36 | elsif match = arg.match(/\A((?.+),|)\s*grep:(?.+)/)
37 | # Legacy style `ls obj, grep: /regexp/`
38 | # Evaluation order should be eval(target) then eval(grep)
39 | target = match[:target] || ''
40 | grep_regexp_code = match[:grep]
41 | else
42 | target = arg.strip
43 | end
44 |
45 | if target.empty?
46 | obj = irb_context.workspace.main
47 | locals = irb_context.workspace.binding.local_variables
48 | else
49 | obj = evaluate(target)
50 | end
51 |
52 | if grep_regexp_code
53 | grep = evaluate(grep_regexp_code)
54 | end
55 |
56 | o = Output.new(grep: grep)
57 |
58 | klass = (obj.class == Class || obj.class == Module ? obj : obj.class)
59 |
60 | o.dump("constants", obj.constants) if obj.respond_to?(:constants)
61 | dump_methods(o, klass, obj)
62 | o.dump("instance variables", obj.instance_variables)
63 | o.dump("class variables", klass.class_variables)
64 | o.dump("locals", locals) if locals
65 | o.print_result
66 | rescue EvaluationError
67 | end
68 |
69 | def dump_methods(o, klass, obj)
70 | singleton_class = begin obj.singleton_class; rescue TypeError; nil end
71 | dumped_mods = Array.new
72 | ancestors = klass.ancestors
73 | ancestors = ancestors.reject { |c| c >= Object } if klass < Object
74 | singleton_ancestors = (singleton_class&.ancestors || []).reject { |c| c >= Class }
75 |
76 | # singleton_class' ancestors should be at the front
77 | maps = class_method_map(singleton_ancestors, dumped_mods) + class_method_map(ancestors, dumped_mods)
78 | maps.each do |mod, methods|
79 | name = mod == singleton_class ? "#{klass}.methods" : "#{mod}#methods"
80 | o.dump(name, methods)
81 | end
82 | end
83 |
84 | def class_method_map(classes, dumped_mods)
85 | dumped_methods = Array.new
86 | classes.map do |mod|
87 | next if dumped_mods.include? mod
88 |
89 | dumped_mods << mod
90 |
91 | methods = mod.public_instance_methods(false).select do |method|
92 | if dumped_methods.include? method
93 | false
94 | else
95 | dumped_methods << method
96 | true
97 | end
98 | end
99 |
100 | [mod, methods]
101 | end.compact
102 | end
103 |
104 | class Output
105 | MARGIN = " "
106 |
107 | def initialize(grep: nil)
108 | @grep = grep
109 | @line_width = screen_width - MARGIN.length # right padding
110 | @io = StringIO.new
111 | end
112 |
113 | def print_result
114 | Pager.page_content(@io.string)
115 | end
116 |
117 | def dump(name, strs)
118 | strs = strs.grep(@grep) if @grep
119 | strs = strs.sort
120 | return if strs.empty?
121 |
122 | # Attempt a single line
123 | @io.print "#{Color.colorize(name, [:BOLD, :BLUE])}: "
124 | if fits_on_line?(strs, cols: strs.size, offset: "#{name}: ".length)
125 | @io.puts strs.join(MARGIN)
126 | return
127 | end
128 | @io.puts
129 |
130 | # Dump with the largest # of columns that fits on a line
131 | cols = strs.size
132 | until fits_on_line?(strs, cols: cols, offset: MARGIN.length) || cols == 1
133 | cols -= 1
134 | end
135 | widths = col_widths(strs, cols: cols)
136 | strs.each_slice(cols) do |ss|
137 | @io.puts ss.map.with_index { |s, i| "#{MARGIN}%-#{widths[i]}s" % s }.join
138 | end
139 | end
140 |
141 | private
142 |
143 | def fits_on_line?(strs, cols:, offset: 0)
144 | width = col_widths(strs, cols: cols).sum + MARGIN.length * (cols - 1)
145 | width <= @line_width - offset
146 | end
147 |
148 | def col_widths(strs, cols:)
149 | cols.times.map do |col|
150 | (col...strs.size).step(cols).map do |i|
151 | strs[i].length
152 | end.max
153 | end
154 | end
155 |
156 | def screen_width
157 | Reline.get_screen_size.last
158 | rescue Errno::EINVAL # in `winsize': Invalid argument -
159 | 80
160 | end
161 | end
162 | private_constant :Output
163 | end
164 | end
165 |
166 | # :startdoc:
167 | end
168 |
--------------------------------------------------------------------------------
/lib/irb/command/measure.rb:
--------------------------------------------------------------------------------
1 | module IRB
2 | # :stopdoc:
3 |
4 | module Command
5 | class Measure < Base
6 | include RubyArgsExtractor
7 |
8 | category "Misc"
9 | description "`measure` enables the mode to measure processing time. `measure :off` disables it."
10 |
11 | def initialize(*args)
12 | super(*args)
13 | end
14 |
15 | def execute(arg)
16 | if arg&.match?(/^do$|^do[^\w]|^\{/)
17 | warn 'Configure IRB.conf[:MEASURE_PROC] to add custom measure methods.'
18 | return
19 | end
20 | args, kwargs = ruby_args(arg)
21 | execute_internal(*args, **kwargs)
22 | end
23 |
24 | def execute_internal(type = nil, arg = nil)
25 | # Please check IRB.init_config in lib/irb/init.rb that sets
26 | # IRB.conf[:MEASURE_PROC] to register default "measure" methods,
27 | # "measure :time" (abbreviated as "measure") and "measure :stackprof".
28 |
29 | case type
30 | when :off
31 | IRB.unset_measure_callback(arg)
32 | when :list
33 | IRB.conf[:MEASURE_CALLBACKS].each do |type_name, _, arg_val|
34 | puts "- #{type_name}" + (arg_val ? "(#{arg_val.inspect})" : '')
35 | end
36 | when :on
37 | added = IRB.set_measure_callback(arg)
38 | puts "#{added[0]} is added." if added
39 | else
40 | added = IRB.set_measure_callback(type, arg)
41 | puts "#{added[0]} is added." if added
42 | end
43 | nil
44 | end
45 | end
46 | end
47 |
48 | # :startdoc:
49 | end
50 |
--------------------------------------------------------------------------------
/lib/irb/command/next.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "debug"
4 |
5 | module IRB
6 | # :stopdoc:
7 |
8 | module Command
9 | class Next < DebugCommand
10 | def execute(arg)
11 | execute_debug_command(do_cmds: "next #{arg}")
12 | end
13 | end
14 | end
15 |
16 | # :startdoc:
17 | end
18 |
--------------------------------------------------------------------------------
/lib/irb/command/pushws.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # change-ws.rb -
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | require_relative "../ext/workspaces"
8 |
9 | module IRB
10 | # :stopdoc:
11 |
12 | module Command
13 | class Workspaces < Base
14 | category "Workspace"
15 | description "Show workspaces."
16 |
17 | def execute(_arg)
18 | inspection_resuls = irb_context.instance_variable_get(:@workspace_stack).map do |ws|
19 | truncated_inspect(ws.main)
20 | end
21 |
22 | puts "[" + inspection_resuls.join(", ") + "]"
23 | end
24 |
25 | private
26 |
27 | def truncated_inspect(obj)
28 | obj_inspection = obj.inspect
29 |
30 | if obj_inspection.size > 20
31 | obj_inspection = obj_inspection[0, 19] + "...>"
32 | end
33 |
34 | obj_inspection
35 | end
36 | end
37 |
38 | class PushWorkspace < Workspaces
39 | category "Workspace"
40 | description "Push an object to the workspace stack."
41 |
42 | def execute(arg)
43 | if arg.empty?
44 | irb_context.push_workspace
45 | else
46 | obj = eval(arg, irb_context.workspace.binding)
47 | irb_context.push_workspace(obj)
48 | end
49 | super
50 | end
51 | end
52 |
53 | class PopWorkspace < Workspaces
54 | category "Workspace"
55 | description "Pop a workspace from the workspace stack."
56 |
57 | def execute(_arg)
58 | irb_context.pop_workspace
59 | super
60 | end
61 | end
62 | end
63 |
64 | # :startdoc:
65 | end
66 |
--------------------------------------------------------------------------------
/lib/irb/command/show_doc.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module IRB
4 | module Command
5 | class ShowDoc < Base
6 | include RubyArgsExtractor
7 |
8 | category "Context"
9 | description "Look up documentation with RI."
10 |
11 | help_message <<~HELP_MESSAGE
12 | Usage: show_doc [name]
13 |
14 | When name is provided, IRB will look up the documentation for the given name.
15 | When no name is provided, a RI session will be started.
16 |
17 | Examples:
18 |
19 | show_doc
20 | show_doc Array
21 | show_doc Array#each
22 |
23 | HELP_MESSAGE
24 |
25 | def execute(arg)
26 | # Accept string literal for backward compatibility
27 | name = unwrap_string_literal(arg)
28 | require 'rdoc/ri/driver'
29 |
30 | unless ShowDoc.const_defined?(:Ri)
31 | opts = RDoc::RI::Driver.process_args([])
32 | ShowDoc.const_set(:Ri, RDoc::RI::Driver.new(opts))
33 | end
34 |
35 | if name.nil?
36 | Ri.interactive
37 | else
38 | begin
39 | Ri.display_name(name)
40 | rescue RDoc::RI::Error
41 | puts $!.message
42 | end
43 | end
44 |
45 | nil
46 | rescue LoadError, SystemExit
47 | warn "Can't display document because `rdoc` is not installed."
48 | end
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/lib/irb/command/show_source.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "../source_finder"
4 | require_relative "../pager"
5 | require_relative "../color"
6 |
7 | module IRB
8 | module Command
9 | class ShowSource < Base
10 | include RubyArgsExtractor
11 |
12 | category "Context"
13 | description "Show the source code of a given method, class/module, or constant."
14 |
15 | help_message <<~HELP_MESSAGE
16 | Usage: show_source [target] [-s]
17 |
18 | -s Show the super method. You can stack it like `-ss` to show the super of the super, etc.
19 |
20 | Examples:
21 |
22 | show_source Foo
23 | show_source Foo#bar
24 | show_source Foo#bar -s
25 | show_source Foo.baz
26 | show_source Foo::BAR
27 | HELP_MESSAGE
28 |
29 | def execute(arg)
30 | # Accept string literal for backward compatibility
31 | str = unwrap_string_literal(arg)
32 | unless str.is_a?(String)
33 | puts "Error: Expected a string but got #{str.inspect}"
34 | return
35 | end
36 |
37 | str, esses = str.split(" -")
38 | super_level = esses ? esses.count("s") : 0
39 | source = SourceFinder.new(@irb_context).find_source(str, super_level)
40 |
41 | if source
42 | show_source(source)
43 | elsif super_level > 0
44 | puts "Error: Couldn't locate a super definition for #{str}"
45 | else
46 | puts "Error: Couldn't locate a definition for #{str}"
47 | end
48 | nil
49 | end
50 |
51 | private
52 |
53 | def show_source(source)
54 | if source.binary_file?
55 | content = "\n#{bold('Defined in binary file')}: #{source.file}\n\n"
56 | else
57 | code = source.colorized_content || 'Source not available'
58 | content = <<~CONTENT
59 |
60 | #{bold("From")}: #{source.file}:#{source.line}
61 |
62 | #{code.chomp}
63 |
64 | CONTENT
65 | end
66 | Pager.page_content(content)
67 | end
68 |
69 | def bold(str)
70 | Color.colorize(str, [:BOLD])
71 | end
72 | end
73 | end
74 | end
75 |
--------------------------------------------------------------------------------
/lib/irb/command/step.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "debug"
4 |
5 | module IRB
6 | # :stopdoc:
7 |
8 | module Command
9 | class Step < DebugCommand
10 | def execute(arg)
11 | execute_debug_command(do_cmds: "step #{arg}")
12 | end
13 | end
14 | end
15 |
16 | # :startdoc:
17 | end
18 |
--------------------------------------------------------------------------------
/lib/irb/command/subirb.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # multi.rb -
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | module IRB
8 | # :stopdoc:
9 |
10 | module Command
11 | class MultiIRBCommand < Base
12 | include RubyArgsExtractor
13 |
14 | private
15 |
16 | def print_deprecated_warning
17 | warn <<~MSG
18 | Multi-irb commands are deprecated and will be removed in IRB 2.0.0. Please use workspace commands instead.
19 | If you have any use case for multi-irb, please leave a comment at https://github.com/ruby/irb/issues/653
20 | MSG
21 | end
22 |
23 | def extend_irb_context
24 | # this extension patches IRB context like IRB.CurrentContext
25 | require_relative "../ext/multi-irb"
26 | end
27 |
28 | def print_debugger_warning
29 | warn "Multi-IRB commands are not available when the debugger is enabled."
30 | end
31 | end
32 |
33 | class IrbCommand < MultiIRBCommand
34 | category "Multi-irb (DEPRECATED)"
35 | description "Start a child IRB."
36 |
37 | def execute(arg)
38 | args, kwargs = ruby_args(arg)
39 | execute_internal(*args, **kwargs)
40 | end
41 |
42 | def execute_internal(*obj)
43 | print_deprecated_warning
44 |
45 | if irb_context.with_debugger
46 | print_debugger_warning
47 | return
48 | end
49 |
50 | extend_irb_context
51 | IRB.irb(nil, *obj)
52 | puts IRB.JobManager.inspect
53 | end
54 | end
55 |
56 | class Jobs < MultiIRBCommand
57 | category "Multi-irb (DEPRECATED)"
58 | description "List of current sessions."
59 |
60 | def execute(_arg)
61 | print_deprecated_warning
62 |
63 | if irb_context.with_debugger
64 | print_debugger_warning
65 | return
66 | end
67 |
68 | extend_irb_context
69 | puts IRB.JobManager.inspect
70 | end
71 | end
72 |
73 | class Foreground < MultiIRBCommand
74 | category "Multi-irb (DEPRECATED)"
75 | description "Switches to the session of the given number."
76 |
77 | def execute(arg)
78 | args, kwargs = ruby_args(arg)
79 | execute_internal(*args, **kwargs)
80 | end
81 |
82 | def execute_internal(key = nil)
83 | print_deprecated_warning
84 |
85 | if irb_context.with_debugger
86 | print_debugger_warning
87 | return
88 | end
89 |
90 | extend_irb_context
91 |
92 | raise CommandArgumentError.new("Please specify the id of target IRB job (listed in the `jobs` command).") unless key
93 | IRB.JobManager.switch(key)
94 | puts IRB.JobManager.inspect
95 | end
96 | end
97 |
98 | class Kill < MultiIRBCommand
99 | category "Multi-irb (DEPRECATED)"
100 | description "Kills the session with the given number."
101 |
102 | def execute(arg)
103 | args, kwargs = ruby_args(arg)
104 | execute_internal(*args, **kwargs)
105 | end
106 |
107 | def execute_internal(*keys)
108 | print_deprecated_warning
109 |
110 | if irb_context.with_debugger
111 | print_debugger_warning
112 | return
113 | end
114 |
115 | extend_irb_context
116 | IRB.JobManager.kill(*keys)
117 | puts IRB.JobManager.inspect
118 | end
119 | end
120 | end
121 |
122 | # :startdoc:
123 | end
124 |
--------------------------------------------------------------------------------
/lib/irb/command/whereami.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module IRB
4 | # :stopdoc:
5 |
6 | module Command
7 | class Whereami < Base
8 | category "Context"
9 | description "Show the source code around binding.irb again."
10 |
11 | def execute(_arg)
12 | code = irb_context.workspace.code_around_binding
13 | if code
14 | puts code
15 | else
16 | puts "The current context doesn't have code."
17 | end
18 | end
19 | end
20 | end
21 |
22 | # :startdoc:
23 | end
24 |
--------------------------------------------------------------------------------
/lib/irb/debug.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module IRB
4 | module Debug
5 | IRB_DIR = File.expand_path('..', __dir__)
6 |
7 | class << self
8 | def insert_debug_break(pre_cmds: nil, do_cmds: nil)
9 | options = { oneshot: true, hook_call: false }
10 |
11 | if pre_cmds || do_cmds
12 | options[:command] = ['irb', pre_cmds, do_cmds]
13 | end
14 | if DEBUGGER__::LineBreakpoint.instance_method(:initialize).parameters.include?([:key, :skip_src])
15 | options[:skip_src] = true
16 | end
17 |
18 | # To make debugger commands like `next` or `continue` work without asking
19 | # the user to quit IRB after that, we need to exit IRB first and then hit
20 | # a TracePoint on #debug_break.
21 | file, lineno = IRB::Irb.instance_method(:debug_break).source_location
22 | DEBUGGER__::SESSION.add_line_breakpoint(file, lineno + 1, **options)
23 | end
24 |
25 | def setup(irb)
26 | # When debug session is not started at all
27 | unless defined?(DEBUGGER__::SESSION)
28 | begin
29 | require "debug/session"
30 | rescue LoadError # debug.gem is not written in Gemfile
31 | return false unless load_bundled_debug_gem
32 | end
33 | DEBUGGER__::CONFIG.set_config
34 | configure_irb_for_debugger(irb)
35 |
36 | DEBUGGER__.initialize_session{ IRB::Debug::UI.new(irb) }
37 | end
38 |
39 | # When debug session was previously started but not by IRB
40 | if defined?(DEBUGGER__::SESSION) && !irb.context.with_debugger
41 | configure_irb_for_debugger(irb)
42 | DEBUGGER__::SESSION.reset_ui(IRB::Debug::UI.new(irb))
43 | end
44 |
45 | # Apply patches to debug gem so it skips IRB frames
46 | unless DEBUGGER__.respond_to?(:capture_frames_without_irb)
47 | DEBUGGER__.singleton_class.send(:alias_method, :capture_frames_without_irb, :capture_frames)
48 |
49 | def DEBUGGER__.capture_frames(*args)
50 | frames = capture_frames_without_irb(*args)
51 | frames.reject! do |frame|
52 | frame.realpath&.start_with?(IRB_DIR) || frame.path == ""
53 | end
54 | frames
55 | end
56 |
57 | DEBUGGER__::ThreadClient.prepend(SkipPathHelperForIRB)
58 | end
59 |
60 | if !DEBUGGER__::CONFIG[:no_hint] && irb.context.io.is_a?(RelineInputMethod)
61 | Reline.output_modifier_proc = proc do |input, complete:|
62 | unless input.strip.empty?
63 | cmd = input[/\S+/]
64 |
65 | if !complete && DEBUGGER__.commands.key?(cmd)
66 | input = input.sub(/\n$/, " # debug command\n")
67 | end
68 | end
69 |
70 | irb.context.colorize_input(input, complete: complete)
71 | end
72 | end
73 |
74 | true
75 | end
76 |
77 | private
78 |
79 | def configure_irb_for_debugger(irb)
80 | require 'irb/debug/ui'
81 | IRB.instance_variable_set(:@debugger_irb, irb)
82 | irb.context.with_debugger = true
83 | irb.context.irb_name += ":rdbg"
84 | irb.context.io.load_history if irb.context.io.class < HistorySavingAbility
85 | end
86 |
87 | module SkipPathHelperForIRB
88 | def skip_internal_path?(path)
89 | # The latter can be removed once https://github.com/ruby/debug/issues/866 is resolved
90 | super || path.match?(IRB_DIR) || path.match?('')
91 | end
92 | end
93 |
94 | # This is used when debug.gem is not written in Gemfile. Even if it's not
95 | # installed by `bundle install`, debug.gem is installed by default because
96 | # it's a bundled gem. This method tries to activate and load that.
97 | def load_bundled_debug_gem
98 | # Discover latest debug.gem under GEM_PATH
99 | debug_gem = Gem.paths.path.flat_map { |path| Dir.glob("#{path}/gems/debug-*") }.select do |path|
100 | File.basename(path).match?(/\Adebug-\d+\.\d+\.\d+(\w+)?\z/)
101 | end.sort_by do |path|
102 | Gem::Version.new(File.basename(path).delete_prefix('debug-'))
103 | end.last
104 | return false unless debug_gem
105 |
106 | # Discover debug/debug.so under extensions for Ruby 3.2+
107 | ext_name = "/debug/debug.#{RbConfig::CONFIG['DLEXT']}"
108 | ext_path = Gem.paths.path.flat_map do |path|
109 | Dir.glob("#{path}/extensions/**/#{File.basename(debug_gem)}#{ext_name}")
110 | end.first
111 |
112 | # Attempt to forcibly load the bundled gem
113 | if ext_path
114 | $LOAD_PATH << ext_path.delete_suffix(ext_name)
115 | end
116 | $LOAD_PATH << "#{debug_gem}/lib"
117 | begin
118 | require "debug/session"
119 | puts "Loaded #{File.basename(debug_gem)}"
120 | true
121 | rescue LoadError
122 | false
123 | end
124 | end
125 | end
126 | end
127 | end
128 |
--------------------------------------------------------------------------------
/lib/irb/debug/ui.rb:
--------------------------------------------------------------------------------
1 | require 'io/console/size'
2 | require 'debug/console'
3 |
4 | module IRB
5 | module Debug
6 | class UI < DEBUGGER__::UI_Base
7 | def initialize(irb)
8 | @irb = irb
9 | end
10 |
11 | def remote?
12 | false
13 | end
14 |
15 | def activate session, on_fork: false
16 | end
17 |
18 | def deactivate
19 | end
20 |
21 | def width
22 | if (w = IO.console_size[1]) == 0 # for tests PTY
23 | 80
24 | else
25 | w
26 | end
27 | end
28 |
29 | def quit n
30 | yield
31 | exit n
32 | end
33 |
34 | def ask prompt
35 | setup_interrupt do
36 | print prompt
37 | ($stdin.gets || '').strip
38 | end
39 | end
40 |
41 | def puts str = nil
42 | case str
43 | when Array
44 | str.each{|line|
45 | $stdout.puts line.chomp
46 | }
47 | when String
48 | Pager.page_content(str, retain_content: true)
49 | when nil
50 | $stdout.puts
51 | end
52 | end
53 |
54 | def readline _
55 | setup_interrupt do
56 | tc = DEBUGGER__::SESSION.instance_variable_get(:@tc)
57 | cmd = @irb.debug_readline(tc.current_frame.eval_binding || TOPLEVEL_BINDING)
58 |
59 | case cmd
60 | when nil # when user types C-d
61 | "continue"
62 | else
63 | cmd
64 | end
65 | end
66 | end
67 |
68 | def setup_interrupt
69 | DEBUGGER__::SESSION.intercept_trap_sigint false do
70 | current_thread = Thread.current # should be session_server thread
71 |
72 | prev_handler = trap(:INT){
73 | current_thread.raise Interrupt
74 | }
75 |
76 | yield
77 | ensure
78 | trap(:INT, prev_handler)
79 | end
80 | end
81 |
82 | def after_fork_parent
83 | parent_pid = Process.pid
84 |
85 | at_exit{
86 | DEBUGGER__::SESSION.intercept_trap_sigint_end
87 | trap(:SIGINT, :IGNORE)
88 |
89 | if Process.pid == parent_pid
90 | # only check child process from its parent
91 | begin
92 | # wait for all child processes to keep terminal
93 | Process.waitpid
94 | rescue Errno::ESRCH, Errno::ECHILD
95 | end
96 | end
97 | }
98 | end
99 | end
100 | end
101 | end
102 |
--------------------------------------------------------------------------------
/lib/irb/easter-egg.rb:
--------------------------------------------------------------------------------
1 | require "reline"
2 |
3 | module IRB
4 | class << self
5 | class Vec
6 | def initialize(x, y, z)
7 | @x, @y, @z = x, y, z
8 | end
9 |
10 | attr_reader :x, :y, :z
11 |
12 | def sub(other)
13 | Vec.new(@x - other.x, @y - other.y, @z - other.z)
14 | end
15 |
16 | def dot(other)
17 | @x*other.x + @y*other.y + @z*other.z
18 | end
19 |
20 | def cross(other)
21 | ox, oy, oz = other.x, other.y, other.z
22 | Vec.new(@y*oz-@z*oy, @z*ox-@x*oz, @x*oy-@y*ox)
23 | end
24 |
25 | def normalize
26 | r = Math.sqrt(self.dot(self))
27 | Vec.new(@x / r, @y / r, @z / r)
28 | end
29 | end
30 |
31 | class Canvas
32 | def initialize((h, w))
33 | @data = (0..h-2).map { [0] * w }
34 | @scale = [w / 2.0, h-2].min
35 | @center = Complex(w / 2, h-2)
36 | end
37 |
38 | def line((x1, y1), (x2, y2))
39 | p1 = Complex(x1, y1) / 2 * @scale + @center
40 | p2 = Complex(x2, y2) / 2 * @scale + @center
41 | line0(p1, p2)
42 | end
43 |
44 | private def line0(p1, p2)
45 | mid = (p1 + p2) / 2
46 | if (p1 - p2).abs < 1
47 | x, y = mid.rect
48 | @data[y / 2][x] |= (y % 2 > 1 ? 2 : 1)
49 | else
50 | line0(p1, mid)
51 | line0(p2, mid)
52 | end
53 | end
54 |
55 | def draw
56 | @data.each {|row| row.fill(0) }
57 | yield
58 | @data.map {|row| row.map {|n| " ',;"[n] }.join }.join("\n")
59 | end
60 | end
61 |
62 | class RubyModel
63 | def initialize
64 | @faces = init_ruby_model
65 | end
66 |
67 | def init_ruby_model
68 | cap_vertices = (0..5).map {|i| Vec.new(*Complex.polar(1, i * Math::PI / 3).rect, 1) }
69 | middle_vertices = (0..5).map {|i| Vec.new(*Complex.polar(2, (i + 0.5) * Math::PI / 3).rect, 0) }
70 | bottom_vertex = Vec.new(0, 0, -2)
71 |
72 | faces = [cap_vertices]
73 | 6.times do |j|
74 | i = j-1
75 | faces << [cap_vertices[i], middle_vertices[i], cap_vertices[j]]
76 | faces << [cap_vertices[j], middle_vertices[i], middle_vertices[j]]
77 | faces << [middle_vertices[i], bottom_vertex, middle_vertices[j]]
78 | end
79 |
80 | faces
81 | end
82 |
83 | def render_frame(i)
84 | angle = i / 10.0
85 | dir = Vec.new(*Complex.polar(1, angle).rect, Math.sin(angle)).normalize
86 | dir2 = Vec.new(*Complex.polar(1, angle - Math::PI/2).rect, 0)
87 | up = dir.cross(dir2)
88 | nm = dir.cross(up)
89 | @faces.each do |vertices|
90 | v0, v1, v2, = vertices
91 | if v1.sub(v0).cross(v2.sub(v0)).dot(dir) > 0
92 | points = vertices.map {|p| [nm.dot(p), up.dot(p)] }
93 | (points + [points[0]]).each_cons(2) do |p1, p2|
94 | yield p1, p2
95 | end
96 | end
97 | end
98 | end
99 | end
100 |
101 | private def easter_egg_logo(type)
102 | @easter_egg_logos ||= File.read(File.join(__dir__, 'ruby_logo.aa'), encoding: 'UTF-8:UTF-8')
103 | .split(/TYPE: ([A-Z_]+)\n/)[1..]
104 | .each_slice(2)
105 | .to_h
106 | @easter_egg_logos[type.to_s.upcase]
107 | end
108 |
109 | private def easter_egg(type = nil)
110 | print "\e[?1049h"
111 | type ||= [:logo, :dancing].sample
112 | case type
113 | when :logo
114 | Pager.page do |io|
115 | logo_type = STDOUT.external_encoding == Encoding::UTF_8 ? :unicode_large : :ascii_large
116 | io.write easter_egg_logo(logo_type)
117 | STDIN.raw { STDIN.getc } if io == STDOUT
118 | end
119 | when :dancing
120 | STDOUT.cooked do
121 | interrupted = false
122 | prev_trap = trap("SIGINT") { interrupted = true }
123 | canvas = Canvas.new(Reline.get_screen_size)
124 | Reline::IOGate.set_winch_handler do
125 | canvas = Canvas.new(Reline.get_screen_size)
126 | end
127 | ruby_model = RubyModel.new
128 | print "\e[?25l" # hide cursor
129 | 0.step do |i| # TODO (0..).each needs Ruby 2.6 or later
130 | buff = canvas.draw do
131 | ruby_model.render_frame(i) do |p1, p2|
132 | canvas.line(p1, p2)
133 | end
134 | end
135 | buff[0, 20] = "\e[0mPress Ctrl+C to stop\e[31m\e[1m"
136 | print "\e[H" + buff
137 | sleep 0.05
138 | break if interrupted
139 | end
140 | rescue Interrupt
141 | ensure
142 | print "\e[?25h" # show cursor
143 | trap("SIGINT", prev_trap)
144 | end
145 | end
146 | ensure
147 | print "\e[0m\e[?1049l"
148 | end
149 | end
150 | end
151 |
152 | IRB.__send__(:easter_egg, ARGV[0]&.to_sym) if $0 == __FILE__
153 |
--------------------------------------------------------------------------------
/lib/irb/ext/change-ws.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # irb/ext/cb.rb -
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | module IRB # :nodoc:
8 | class Context
9 |
10 | # Inherited from +TOPLEVEL_BINDING+.
11 | def home_workspace
12 | if defined? @home_workspace
13 | @home_workspace
14 | else
15 | @home_workspace = workspace
16 | end
17 | end
18 |
19 | # Changes the current workspace to given object or binding.
20 | #
21 | # If the optional argument is omitted, the workspace will be
22 | # #home_workspace which is inherited from +TOPLEVEL_BINDING+ or the main
23 | # object, IRB.conf[:MAIN_CONTEXT]
when irb was initialized.
24 | #
25 | # See IRB::WorkSpace.new for more information.
26 | def change_workspace(*_main)
27 | if _main.empty?
28 | replace_workspace(home_workspace)
29 | return main
30 | end
31 |
32 | workspace = WorkSpace.new(_main[0])
33 | replace_workspace(workspace)
34 | workspace.load_helper_methods_to_main
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/lib/irb/ext/eval_history.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # history.rb -
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | module IRB # :nodoc:
8 |
9 | class Context
10 |
11 | NOPRINTING_IVARS.push "@eval_history_values"
12 |
13 | # See #set_last_value
14 | alias _set_last_value set_last_value
15 |
16 | def set_last_value(value)
17 | _set_last_value(value)
18 |
19 | if defined?(@eval_history) && @eval_history
20 | @eval_history_values.push @line_no, @last_value
21 | workspace.evaluate "__ = IRB.CurrentContext.instance_eval{@eval_history_values}"
22 | end
23 |
24 | @last_value
25 | end
26 |
27 | remove_method :eval_history= if method_defined?(:eval_history=)
28 | # The command result history limit. This method is not available until
29 | # #eval_history= was called with non-nil value (directly or via
30 | # setting IRB.conf[:EVAL_HISTORY]
in .irbrc
).
31 | attr_reader :eval_history
32 | # Sets command result history limit. Default value is set from
33 | # IRB.conf[:EVAL_HISTORY]
.
34 | #
35 | # +no+ is an Integer or +nil+.
36 | #
37 | # Returns +no+ of history items if greater than 0.
38 | #
39 | # If +no+ is 0, the number of history items is unlimited.
40 | #
41 | # If +no+ is +nil+, execution result history isn't used (default).
42 | #
43 | # EvalHistory values are available via __
variable, see
44 | # IRB::EvalHistory.
45 | def eval_history=(no)
46 | if no
47 | if defined?(@eval_history) && @eval_history
48 | @eval_history_values.size(no)
49 | else
50 | @eval_history_values = EvalHistory.new(no)
51 | IRB.conf[:__TMP__EHV__] = @eval_history_values
52 | workspace.evaluate("__ = IRB.conf[:__TMP__EHV__]")
53 | IRB.conf.delete(:__TMP_EHV__)
54 | end
55 | else
56 | @eval_history_values = nil
57 | end
58 | @eval_history = no
59 | end
60 | end
61 |
62 | # Represents history of results of previously evaluated commands.
63 | #
64 | # Available via __
variable, only if IRB.conf[:EVAL_HISTORY]
65 | # or IRB::CurrentContext().eval_history
is non-nil integer value
66 | # (by default it is +nil+).
67 | #
68 | # Example (in `irb`):
69 | #
70 | # # Initialize history
71 | # IRB::CurrentContext().eval_history = 10
72 | # # => 10
73 | #
74 | # # Perform some commands...
75 | # 1 + 2
76 | # # => 3
77 | # puts 'x'
78 | # # x
79 | # # => nil
80 | # raise RuntimeError
81 | # # ...error raised
82 | #
83 | # # Inspect history (format is "- ":
84 | # __
85 | # # => 1 10
86 | # # 2 3
87 | # # 3 nil
88 | #
89 | # __[1]
90 | # # => 10
91 | #
92 | class EvalHistory
93 |
94 | def initialize(size = 16) # :nodoc:
95 | @size = size
96 | @contents = []
97 | end
98 |
99 | def size(size) # :nodoc:
100 | if size != 0 && size < @size
101 | @contents = @contents[@size - size .. @size]
102 | end
103 | @size = size
104 | end
105 |
106 | # Get one item of the content (both positive and negative indexes work).
107 | def [](idx)
108 | begin
109 | if idx >= 0
110 | @contents.find{|no, val| no == idx}[1]
111 | else
112 | @contents[idx][1]
113 | end
114 | rescue NameError
115 | nil
116 | end
117 | end
118 |
119 | def push(no, val) # :nodoc:
120 | @contents.push [no, val]
121 | @contents.shift if @size != 0 && @contents.size > @size
122 | end
123 |
124 | alias real_inspect inspect
125 |
126 | def inspect # :nodoc:
127 | if @contents.empty?
128 | return real_inspect
129 | end
130 |
131 | unless (last = @contents.pop)[1].equal?(self)
132 | @contents.push last
133 | last = nil
134 | end
135 | str = @contents.collect{|no, val|
136 | if val.equal?(self)
137 | "#{no} ...self-history..."
138 | else
139 | "#{no} #{val.inspect}"
140 | end
141 | }.join("\n")
142 | if str == ""
143 | str = "Empty."
144 | end
145 | @contents.push last if last
146 | str
147 | end
148 | end
149 | end
150 |
--------------------------------------------------------------------------------
/lib/irb/ext/loader.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # loader.rb -
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | module IRB # :nodoc:
8 | # Raised in the event of an exception in a file loaded from an Irb session
9 | class LoadAbort < Exception;end
10 |
11 | # Provides a few commands for loading files within an irb session.
12 | #
13 | # See ExtendCommandBundle for more information.
14 | module IrbLoader
15 |
16 | alias ruby_load load
17 | alias ruby_require require
18 |
19 | # Loads the given file similarly to Kernel#load
20 | def irb_load(fn, priv = nil)
21 | path = search_file_from_ruby_path(fn)
22 | raise LoadError, "No such file to load -- #{fn}" unless path
23 |
24 | load_file(path, priv)
25 | end
26 |
27 | def search_file_from_ruby_path(fn) # :nodoc:
28 | if File.absolute_path?(fn)
29 | return fn if File.exist?(fn)
30 | return nil
31 | end
32 |
33 | for path in $:
34 | if File.exist?(f = File.join(path, fn))
35 | return f
36 | end
37 | end
38 | return nil
39 | end
40 |
41 | # Loads a given file in the current session and displays the source lines
42 | #
43 | # See Irb#suspend_input_method for more information.
44 | def source_file(path)
45 | irb = irb_context.irb
46 | irb.suspend_name(path, File.basename(path)) do
47 | FileInputMethod.open(path) do |io|
48 | irb.suspend_input_method(io) do
49 | |back_io|
50 | irb.signal_status(:IN_LOAD) do
51 | if back_io.kind_of?(FileInputMethod)
52 | irb.eval_input
53 | else
54 | begin
55 | irb.eval_input
56 | rescue LoadAbort
57 | print "load abort!!\n"
58 | end
59 | end
60 | end
61 | end
62 | end
63 | end
64 | end
65 |
66 | # Loads the given file in the current session's context and evaluates it.
67 | #
68 | # See Irb#suspend_input_method for more information.
69 | def load_file(path, priv = nil)
70 | irb = irb_context.irb
71 | irb.suspend_name(path, File.basename(path)) do
72 |
73 | if priv
74 | ws = WorkSpace.new(Module.new)
75 | else
76 | ws = WorkSpace.new
77 | end
78 | irb.suspend_workspace(ws) do
79 | FileInputMethod.open(path) do |io|
80 | irb.suspend_input_method(io) do
81 | |back_io|
82 | irb.signal_status(:IN_LOAD) do
83 | if back_io.kind_of?(FileInputMethod)
84 | irb.eval_input
85 | else
86 | begin
87 | irb.eval_input
88 | rescue LoadAbort
89 | print "load abort!!\n"
90 | end
91 | end
92 | end
93 | end
94 | end
95 | end
96 | end
97 | end
98 |
99 | def old # :nodoc:
100 | back_io = @io
101 | back_path = irb_path
102 | back_name = @irb_name
103 | back_scanner = @irb.scanner
104 | begin
105 | @io = FileInputMethod.new(path)
106 | @irb_name = File.basename(path)
107 | self.irb_path = path
108 | @irb.signal_status(:IN_LOAD) do
109 | if back_io.kind_of?(FileInputMethod)
110 | @irb.eval_input
111 | else
112 | begin
113 | @irb.eval_input
114 | rescue LoadAbort
115 | print "load abort!!\n"
116 | end
117 | end
118 | end
119 | ensure
120 | @io = back_io
121 | @irb_name = back_name
122 | self.irb_path = back_path
123 | @irb.scanner = back_scanner
124 | end
125 | end
126 | end
127 | end
128 |
--------------------------------------------------------------------------------
/lib/irb/ext/tracer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # irb/lib/tracer.rb -
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 | # Loading the gem "tracer" will cause it to extend IRB commands with:
7 | # https://github.com/ruby/tracer/blob/v0.2.2/lib/tracer/irb.rb
8 | begin
9 | require "tracer"
10 | rescue LoadError
11 | $stderr.puts "Tracer extension of IRB is enabled but tracer gem wasn't found."
12 | return # This is about to disable loading below
13 | end
14 |
15 | module IRB
16 | class CallTracer < ::CallTracer
17 | IRB_DIR = File.expand_path('../..', __dir__)
18 |
19 | def skip?(tp)
20 | super || tp.path.match?(IRB_DIR) || tp.path.match?('')
21 | end
22 | end
23 | class WorkSpace
24 | alias __evaluate__ evaluate
25 | # Evaluate the context of this workspace and use the Tracer library to
26 | # output the exact lines of code are being executed in chronological order.
27 | #
28 | # See https://github.com/ruby/tracer for more information.
29 | def evaluate(statements, file = __FILE__, line = __LINE__)
30 | if IRB.conf[:USE_TRACER] == true
31 | CallTracer.new(colorize: Color.colorable?).start do
32 | __evaluate__(statements, file, line)
33 | end
34 | else
35 | __evaluate__(statements, file, line)
36 | end
37 | end
38 | end
39 | end
40 |
--------------------------------------------------------------------------------
/lib/irb/ext/use-loader.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # use-loader.rb -
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | require_relative "../command/load"
8 | require_relative "loader"
9 |
10 | class Object
11 | alias __original__load__IRB_use_loader__ load
12 | alias __original__require__IRB_use_loader__ require
13 | end
14 |
15 | module IRB
16 | module ExtendCommandBundle
17 | remove_method :irb_load if method_defined?(:irb_load)
18 | # Loads the given file similarly to Kernel#load, see IrbLoader#irb_load
19 | def irb_load(*opts, &b)
20 | Command::Load.execute(irb_context, *opts, &b)
21 | end
22 | remove_method :irb_require if method_defined?(:irb_require)
23 | # Loads the given file similarly to Kernel#require
24 | def irb_require(*opts, &b)
25 | Command::Require.execute(irb_context, *opts, &b)
26 | end
27 | end
28 |
29 | class Context
30 |
31 | IRB.conf[:USE_LOADER] = false
32 |
33 | # Returns whether +irb+'s own file reader method is used by
34 | # +load+/+require+ or not.
35 | #
36 | # This mode is globally affected (irb-wide).
37 | def use_loader
38 | IRB.conf[:USE_LOADER]
39 | end
40 |
41 | alias use_loader? use_loader
42 |
43 | remove_method :use_loader= if method_defined?(:use_loader=)
44 | # Sets
IRB.conf[:USE_LOADER]
45 | #
46 | # See #use_loader for more information.
47 | def use_loader=(opt)
48 |
49 | if IRB.conf[:USE_LOADER] != opt
50 | IRB.conf[:USE_LOADER] = opt
51 | if opt
52 | (class< 1
17 | # swap the top two workspaces
18 | previous_workspace, current_workspace = @workspace_stack.pop(2)
19 | @workspace_stack.push current_workspace, previous_workspace
20 | end
21 | else
22 | new_workspace = WorkSpace.new(workspace.binding, _main[0])
23 | @workspace_stack.push new_workspace
24 | new_workspace.load_helper_methods_to_main
25 | end
26 | end
27 |
28 | # Removes the last element from the current #workspaces stack and returns
29 | # it, or +nil+ if the current workspace stack is empty.
30 | #
31 | # Also, see #push_workspace.
32 | def pop_workspace
33 | @workspace_stack.pop if @workspace_stack.size > 1
34 | end
35 | end
36 | end
37 |
--------------------------------------------------------------------------------
/lib/irb/frame.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # frame.rb -
4 | # by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
5 | #
6 |
7 | module IRB
8 | class Frame
9 | class FrameOverflow < StandardError
10 | def initialize
11 | super("frame overflow")
12 | end
13 | end
14 | class FrameUnderflow < StandardError
15 | def initialize
16 | super("frame underflow")
17 | end
18 | end
19 |
20 | # Default number of stack frames
21 | INIT_STACK_TIMES = 3
22 | # Default number of frames offset
23 | CALL_STACK_OFFSET = 3
24 |
25 | # Creates a new stack frame
26 | def initialize
27 | @frames = [TOPLEVEL_BINDING] * INIT_STACK_TIMES
28 | end
29 |
30 | # Used by Kernel#set_trace_func to register each event in the call stack
31 | def trace_func(event, file, line, id, binding)
32 | case event
33 | when 'call', 'class'
34 | @frames.push binding
35 | when 'return', 'end'
36 | @frames.pop
37 | end
38 | end
39 |
40 | # Returns the +n+ number of frames on the call stack from the last frame
41 | # initialized.
42 | #
43 | # Raises FrameUnderflow if there are no frames in the given stack range.
44 | def top(n = 0)
45 | bind = @frames[-(n + CALL_STACK_OFFSET)]
46 | fail FrameUnderflow unless bind
47 | bind
48 | end
49 |
50 | # Returns the +n+ number of frames on the call stack from the first frame
51 | # initialized.
52 | #
53 | # Raises FrameOverflow if there are no frames in the given stack range.
54 | def bottom(n = 0)
55 | bind = @frames[n]
56 | fail FrameOverflow unless bind
57 | bind
58 | end
59 |
60 | # Convenience method for Frame#bottom
61 | def Frame.bottom(n = 0)
62 | @backtrace.bottom(n)
63 | end
64 |
65 | # Convenience method for Frame#top
66 | def Frame.top(n = 0)
67 | @backtrace.top(n)
68 | end
69 |
70 | # Returns the binding context of the caller from the last frame initialized
71 | def Frame.sender
72 | eval "self", @backtrace.top
73 | end
74 |
75 | @backtrace = Frame.new
76 | set_trace_func proc{|event, file, line, id, binding, klass|
77 | @backtrace.trace_func(event, file, line, id, binding)
78 | }
79 | end
80 | end
81 |
--------------------------------------------------------------------------------
/lib/irb/help.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # irb/help.rb - print usage module
4 | # by Keiju ISHITSUKA(keiju@ishitsuka.com)
5 | #
6 |
7 | module IRB
8 | # Outputs the irb help message, see IRB@Command-Line+Options.
9 | def IRB.print_usage # :nodoc:
10 | lc = IRB.conf[:LC_MESSAGES]
11 | path = lc.find("irb/help-message")
12 | space_line = false
13 | File.open(path){|f|
14 | f.each_line do |l|
15 | if /^\s*$/ =~ l
16 | lc.puts l unless space_line
17 | space_line = true
18 | next
19 | end
20 | space_line = false
21 |
22 | l.sub!(/#.*$/, "")
23 | next if /^\s*$/ =~ l
24 | lc.puts l
25 | end
26 | }
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/irb/helper_method.rb:
--------------------------------------------------------------------------------
1 | require_relative "helper_method/base"
2 |
3 | module IRB
4 | module HelperMethod
5 | @helper_methods = {}
6 |
7 | class << self
8 | attr_reader :helper_methods
9 |
10 | def register(name, helper_class)
11 | @helper_methods[name] = helper_class
12 |
13 | if defined?(HelpersContainer)
14 | HelpersContainer.install_helper_methods
15 | end
16 | end
17 |
18 | def all_helper_methods_info
19 | @helper_methods.map do |name, helper_class|
20 | { display_name: name, description: helper_class.description }
21 | end
22 | end
23 | end
24 |
25 | # Default helper_methods
26 | require_relative "helper_method/conf"
27 | register(:conf, HelperMethod::Conf)
28 | end
29 | end
30 |
--------------------------------------------------------------------------------
/lib/irb/helper_method/base.rb:
--------------------------------------------------------------------------------
1 | require "singleton"
2 |
3 | module IRB
4 | module HelperMethod
5 | class Base
6 | include Singleton
7 |
8 | class << self
9 | def description(description = nil)
10 | @description = description if description
11 | @description
12 | end
13 | end
14 | end
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/lib/irb/helper_method/conf.rb:
--------------------------------------------------------------------------------
1 | module IRB
2 | module HelperMethod
3 | class Conf < Base
4 | description "Returns the current IRB context."
5 |
6 | def execute
7 | IRB.CurrentContext
8 | end
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/irb/history.rb:
--------------------------------------------------------------------------------
1 | require "pathname"
2 |
3 | module IRB
4 | module History
5 | DEFAULT_ENTRY_LIMIT = 1000
6 |
7 | class << self
8 | # Integer representation of IRB.conf[:HISTORY_FILE]
.
9 | def save_history
10 | return 0 if IRB.conf[:SAVE_HISTORY] == false
11 | return DEFAULT_ENTRY_LIMIT if IRB.conf[:SAVE_HISTORY] == true
12 | IRB.conf[:SAVE_HISTORY].to_i
13 | end
14 |
15 | def save_history?
16 | !save_history.zero?
17 | end
18 |
19 | def infinite?
20 | save_history.negative?
21 | end
22 |
23 | # Might be nil when HOME and XDG_CONFIG_HOME are not available.
24 | def history_file
25 | if (history_file = IRB.conf[:HISTORY_FILE])
26 | File.expand_path(history_file)
27 | else
28 | IRB.rc_file("_history")
29 | end
30 | end
31 | end
32 | end
33 |
34 | module HistorySavingAbility # :nodoc:
35 | def support_history_saving?
36 | true
37 | end
38 |
39 | def reset_history_counter
40 | @loaded_history_lines = self.class::HISTORY.size
41 | end
42 |
43 | def load_history
44 | history_file = History.history_file
45 | return unless File.exist?(history_file.to_s)
46 |
47 | history = self.class::HISTORY
48 |
49 | File.open(history_file, "r:#{IRB.conf[:LC_MESSAGES].encoding}") do |f|
50 | f.each { |l|
51 | l = l.chomp
52 | if self.class == RelineInputMethod and history.last&.end_with?("\\")
53 | history.last.delete_suffix!("\\")
54 | history.last << "\n" << l
55 | else
56 | history << l
57 | end
58 | }
59 | end
60 | @loaded_history_lines = history.size
61 | @loaded_history_mtime = File.mtime(history_file)
62 | end
63 |
64 | def save_history
65 | return unless History.save_history?
66 | return unless (history_file = History.history_file)
67 | unless ensure_history_file_writable(history_file)
68 | warn <<~WARN
69 | Can't write history to #{History.history_file.inspect} due to insufficient permissions.
70 | Please verify the value of `IRB.conf[:HISTORY_FILE]`. Ensure the folder exists and that both the folder and file (if it exists) are writable.
71 | WARN
72 | return
73 | end
74 |
75 | history = self.class::HISTORY.to_a
76 |
77 | if File.exist?(history_file) &&
78 | File.mtime(history_file) != @loaded_history_mtime
79 | history = history[@loaded_history_lines..-1] if @loaded_history_lines
80 | append_history = true
81 | end
82 |
83 | File.open(history_file, (append_history ? "a" : "w"), 0o600, encoding: IRB.conf[:LC_MESSAGES]&.encoding) do |f|
84 | hist = history.map { |l| l.scrub.split("\n").join("\\\n") }
85 |
86 | unless append_history || History.infinite?
87 | # Check size before slicing because array.last(huge_number) raises RangeError.
88 | hist = hist.last(History.save_history) if hist.size > History.save_history
89 | end
90 |
91 | f.puts(hist)
92 | end
93 | end
94 |
95 | private
96 |
97 | # Returns boolean whether writing to +history_file+ will be possible.
98 | # Permissions of already existing +history_file+ are changed to
99 | # owner-only-readable if necessary [BUG #7694].
100 | def ensure_history_file_writable(history_file)
101 | history_file = Pathname.new(history_file)
102 |
103 | return false unless history_file.dirname.writable?
104 | return true unless history_file.exist?
105 |
106 | begin
107 | if history_file.stat.mode & 0o66 != 0
108 | history_file.chmod 0o600
109 | end
110 | true
111 | rescue Errno::EPERM # no permissions
112 | false
113 | end
114 | end
115 | end
116 | end
117 |
--------------------------------------------------------------------------------
/lib/irb/inspector.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # irb/inspector.rb - inspect methods
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | module IRB # :nodoc:
8 |
9 | # Convenience method to create a new Inspector, using the given +inspect+
10 | # proc, and optional +init+ proc and passes them to Inspector.new
11 | #
12 | # irb(main):001:0> ins = IRB::Inspector(proc{ |v| "omg! #{v}" })
13 | # irb(main):001:0> IRB.CurrentContext.inspect_mode = ins # => omg! #
14 | # irb(main):001:0> "what?" #=> omg! what?
15 | #
16 | def IRB::Inspector(inspect, init = nil)
17 | Inspector.new(inspect, init)
18 | end
19 |
20 | # An irb inspector
21 | #
22 | # In order to create your own custom inspector there are two things you
23 | # should be aware of:
24 | #
25 | # Inspector uses #inspect_value, or +inspect_proc+, for output of return values.
26 | #
27 | # This also allows for an optional #init+, or +init_proc+, which is called
28 | # when the inspector is activated.
29 | #
30 | # Knowing this, you can create a rudimentary inspector as follows:
31 | #
32 | # irb(main):001:0> ins = IRB::Inspector.new(proc{ |v| "omg! #{v}" })
33 | # irb(main):001:0> IRB.CurrentContext.inspect_mode = ins # => omg! #
34 | # irb(main):001:0> "what?" #=> omg! what?
35 | #
36 | class Inspector
37 | KERNEL_INSPECT = Object.instance_method(:inspect)
38 | # Default inspectors available to irb, this includes:
39 | #
40 | # +:pp+:: Using Kernel#pretty_inspect
41 | # +:yaml+:: Using YAML.dump
42 | # +:marshal+:: Using Marshal.dump
43 | INSPECTORS = {}
44 |
45 | class << self
46 | # Determines the inspector to use where +inspector+ is one of the keys passed
47 | # during inspector definition.
48 | def keys_with_inspector(inspector)
49 | INSPECTORS.filter_map {|k, v| k if v == inspector}
50 | end
51 |
52 | # Example
53 | #
54 | # Inspector.def_inspector(key, init_p=nil){|v| v.inspect}
55 | # Inspector.def_inspector([key1,..], init_p=nil){|v| v.inspect}
56 | # Inspector.def_inspector(key, inspector)
57 | # Inspector.def_inspector([key1,...], inspector)
58 | def def_inspector(key, arg=nil, &block)
59 | if block_given?
60 | inspector = IRB::Inspector(block, arg)
61 | else
62 | inspector = arg
63 | end
64 |
65 | case key
66 | when Array
67 | for k in key
68 | def_inspector(k, inspector)
69 | end
70 | when Symbol
71 | INSPECTORS[key] = inspector
72 | INSPECTORS[key.to_s] = inspector
73 | when String
74 | INSPECTORS[key] = inspector
75 | INSPECTORS[key.intern] = inspector
76 | else
77 | INSPECTORS[key] = inspector
78 | end
79 | end
80 | end
81 |
82 | # Creates a new inspector object, using the given +inspect_proc+ when
83 | # output return values in irb.
84 | def initialize(inspect_proc, init_proc = nil)
85 | @init = init_proc
86 | @inspect = inspect_proc
87 | end
88 |
89 | # Proc to call when the inspector is activated, good for requiring
90 | # dependent libraries.
91 | def init
92 | @init.call if @init
93 | end
94 |
95 | def support_stream_output?
96 | second_parameter_type = @inspect.parameters[1]&.first
97 | second_parameter_type == :req || second_parameter_type == :opt
98 | end
99 |
100 | # Proc to call when the input is evaluated and output in irb.
101 | def inspect_value(v, output, colorize: true)
102 | support_stream_output? ? @inspect.call(v, output, colorize: colorize) : output << @inspect.call(v, colorize: colorize)
103 | rescue => e
104 | puts "An error occurred when inspecting the object: #{e.inspect}"
105 |
106 | begin
107 | puts "Result of Kernel#inspect: #{KERNEL_INSPECT.bind_call(v)}"
108 | ''
109 | rescue => e
110 | puts "An error occurred when running Kernel#inspect: #{e.inspect}"
111 | puts e.backtrace.join("\n")
112 | ''
113 | end
114 | end
115 | end
116 |
117 | Inspector.def_inspector([false, :to_s, :raw]){|v| v.to_s}
118 | Inspector.def_inspector([:p, :inspect]){|v, colorize: true|
119 | Color.colorize_code(v.inspect, colorable: colorize && Color.colorable? && Color.inspect_colorable?(v))
120 | }
121 | Inspector.def_inspector([true, :pp, :pretty_inspect], proc{require_relative "color_printer"}){|v, output, colorize: true|
122 | IRB::ColorPrinter.pp(v, output, colorize: colorize)
123 | }
124 | Inspector.def_inspector([:yaml, :YAML], proc{require "yaml"}){|v|
125 | begin
126 | YAML.dump(v)
127 | rescue
128 | puts "(can't dump yaml. use inspect)"
129 | v.inspect
130 | end
131 | }
132 |
133 | Inspector.def_inspector([:marshal, :Marshal, :MARSHAL, Marshal]){|v|
134 | Marshal.dump(v)
135 | }
136 | end
137 |
--------------------------------------------------------------------------------
/lib/irb/lc/error.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # irb/lc/error.rb -
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | module IRB
8 | # :stopdoc:
9 |
10 | class UnrecognizedSwitch < StandardError
11 | def initialize(val)
12 | super("Unrecognized switch: #{val}")
13 | end
14 | end
15 | class CantReturnToNormalMode < StandardError
16 | def initialize
17 | super("Can't return to normal mode.")
18 | end
19 | end
20 | class IllegalParameter < StandardError
21 | def initialize(val)
22 | super("Invalid parameter(#{val}).")
23 | end
24 | end
25 | class IrbAlreadyDead < StandardError
26 | def initialize
27 | super("Irb is already dead.")
28 | end
29 | end
30 | class IrbSwitchedToCurrentThread < StandardError
31 | def initialize
32 | super("Switched to current thread.")
33 | end
34 | end
35 | class NoSuchJob < StandardError
36 | def initialize(val)
37 | super("No such job(#{val}).")
38 | end
39 | end
40 | class CantChangeBinding < StandardError
41 | def initialize(val)
42 | super("Can't change binding to (#{val}).")
43 | end
44 | end
45 | class UndefinedPromptMode < StandardError
46 | def initialize(val)
47 | super("Undefined prompt mode(#{val}).")
48 | end
49 | end
50 |
51 | # :startdoc:
52 | end
53 |
--------------------------------------------------------------------------------
/lib/irb/lc/help-message:
--------------------------------------------------------------------------------
1 | Usage: irb.rb [options] [programfile] [arguments]
2 | -f Don't initialize from configuration file.
3 | -d Set $DEBUG and $VERBOSE to true (same as 'ruby -d').
4 | -r load-module Require load-module (same as 'ruby -r').
5 | -I path Specify $LOAD_PATH directory (same as 'ruby -I').
6 | -U Set external and internal encodings to UTF-8.
7 | -E ex[:in] Set default external (ex) and internal (in) encodings
8 | (same as 'ruby -E').
9 | -w Suppress warnings (same as 'ruby -w').
10 | -W[level=2] Set warning level: 0=silence, 1=medium, 2=verbose
11 | (same as 'ruby -W').
12 | --context-mode n Set n[0-4] to method to create Binding Object,
13 | when new workspace was created.
14 | --extra-doc-dir Add an extra doc dir for the doc dialog.
15 | --echo Show result (default).
16 | --noecho Don't show result.
17 | --echo-on-assignment
18 | Show result on assignment.
19 | --noecho-on-assignment
20 | Don't show result on assignment.
21 | --truncate-echo-on-assignment
22 | Show truncated result on assignment (default).
23 | --inspect Use 'inspect' for output.
24 | --noinspect Don't use 'inspect' for output.
25 | --no-pager Don't use pager.
26 | --multiline Use multiline editor module (default).
27 | --nomultiline Don't use multiline editor module.
28 | --singleline Use single line editor module.
29 | --nosingleline Don't use single line editor module (default).
30 | --colorize Use color-highlighting (default).
31 | --nocolorize Don't use color-highlighting.
32 | --autocomplete Use auto-completion (default).
33 | --noautocomplete Don't use auto-completion.
34 | --regexp-completor
35 | Use regexp based completion (default).
36 | --type-completor Use type based completion.
37 | --prompt prompt-mode, --prompt-mode prompt-mode
38 | Set prompt mode. Pre-defined prompt modes are:
39 | 'default', 'classic', 'simple', 'inf-ruby', 'xmp', 'null'.
40 | --inf-ruby-mode Use prompt appropriate for inf-ruby-mode on emacs.
41 | Suppresses --multiline and --singleline.
42 | --sample-book-mode, --simple-prompt
43 | Set prompt mode to 'simple'.
44 | --noprompt Don't output prompt.
45 | --script Script mode (default, treat first argument as script)
46 | --noscript No script mode (leave arguments in argv)
47 | --single-irb Share self with sub-irb.
48 | --tracer Show stack trace for each command.
49 | --back-trace-limit n[=16]
50 | Display backtrace top n and bottom n.
51 | --verbose Show details.
52 | --noverbose Don't show details.
53 | -v, --version Print the version of irb.
54 | -h, --help Print help.
55 | -- Separate options of irb from the list of command-line args.
56 |
--------------------------------------------------------------------------------
/lib/irb/lc/ja/error.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # irb/lc/ja/error.rb -
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | module IRB
8 | # :stopdoc:
9 |
10 | class UnrecognizedSwitch < StandardError
11 | def initialize(val)
12 | super("スイッチ(#{val})が分りません")
13 | end
14 | end
15 | class CantReturnToNormalMode < StandardError
16 | def initialize
17 | super("Normalモードに戻れません.")
18 | end
19 | end
20 | class IllegalParameter < StandardError
21 | def initialize(val)
22 | super("パラメータ(#{val})が間違っています.")
23 | end
24 | end
25 | class IrbAlreadyDead < StandardError
26 | def initialize
27 | super("Irbは既に死んでいます.")
28 | end
29 | end
30 | class IrbSwitchedToCurrentThread < StandardError
31 | def initialize
32 | super("カレントスレッドに切り替わりました.")
33 | end
34 | end
35 | class NoSuchJob < StandardError
36 | def initialize(val)
37 | super("そのようなジョブ(#{val})はありません.")
38 | end
39 | end
40 | class CantChangeBinding < StandardError
41 | def initialize(val)
42 | super("バインディング(#{val})に変更できません.")
43 | end
44 | end
45 | class UndefinedPromptMode < StandardError
46 | def initialize(val)
47 | super("プロンプトモード(#{val})は定義されていません.")
48 | end
49 | end
50 |
51 | # :startdoc:
52 | end
53 | # vim:fileencoding=utf-8
54 |
--------------------------------------------------------------------------------
/lib/irb/lc/ja/help-message:
--------------------------------------------------------------------------------
1 | Usage: irb.rb [options] [programfile] [arguments]
2 | -f ~/.irbrc を読み込まない.
3 | -d $DEBUG をtrueにする(ruby -d と同じ)
4 | -r load-module ruby -r と同じ.
5 | -I path $LOAD_PATH に path を追加する.
6 | -U ruby -U と同じ.
7 | -E enc ruby -E と同じ.
8 | -w ruby -w と同じ.
9 | -W[level=2] ruby -W と同じ.
10 | --context-mode n 新しいワークスペースを作成した時に関連する Binding
11 | オブジェクトの作成方法を 0 から 4 のいずれかに設定する.
12 | --extra-doc-dir 指定したディレクトリのドキュメントを追加で読み込む.
13 | --echo 実行結果を表示する(デフォルト).
14 | --noecho 実行結果を表示しない.
15 | --echo-on-assignment
16 | 代入結果を表示する.
17 | --noecho-on-assignment
18 | 代入結果を表示しない.
19 | --truncate-echo-on-assignment
20 | truncateされた代入結果を表示する(デフォルト).
21 | --inspect 結果出力にinspectを用いる.
22 | --noinspect 結果出力にinspectを用いない.
23 | --no-pager ページャを使用しない.
24 | --multiline マルチラインエディタを利用する.
25 | --nomultiline マルチラインエディタを利用しない.
26 | --singleline シングルラインエディタを利用する.
27 | --nosingleline シングルラインエディタを利用しない.
28 | --colorize 色付けを利用する.
29 | --nocolorize 色付けを利用しない.
30 | --autocomplete オートコンプリートを利用する.
31 | --noautocomplete オートコンプリートを利用しない.
32 | --regexp-completor
33 | 補完に正規表現を利用する.
34 | --type-completor 補完に型情報を利用する.
35 | --prompt prompt-mode/--prompt-mode prompt-mode
36 | プロンプトモードを切り替える.
37 | 現在定義されているプロンプトモードは,
38 | default, classic, simple, inf-ruby, xmp, null.
39 | --inf-ruby-mode emacsのinf-ruby-mode用のプロンプト表示を行なう. 特
40 | に指定がない限り, シングルラインエディタとマルチラ
41 | インエディタは使わなくなる.
42 | --sample-book-mode/--simple-prompt
43 | 非常にシンプルなプロンプトを用いるモードです.
44 | --noprompt プロンプト表示を行なわない.
45 | --script スクリプトモード(最初の引数をスクリプトファイルとして扱う、デフォルト)
46 | --noscript 引数をargvとして扱う.
47 | --single-irb irb 中で self を実行して得られるオブジェクトをサ
48 | ブ irb と共有する.
49 | --tracer コマンド実行時にトレースを行なう.
50 | --back-trace-limit n
51 | バックトレース表示をバックトレースの頭から n, 後ろ
52 | からnだけ行なう. デフォルトは16
53 |
54 | --verbose 詳細なメッセージを出力する.
55 | --noverbose 詳細なメッセージを出力しない(デフォルト).
56 | -v, --version irbのバージョンを表示する.
57 | -h, --help irb のヘルプを表示する.
58 | -- 以降のコマンドライン引数をオプションとして扱わない.
59 |
--------------------------------------------------------------------------------
/lib/irb/locale.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # irb/locale.rb - internationalization module
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | module IRB # :nodoc:
8 | class Locale
9 |
10 | LOCALE_NAME_RE = %r[
11 | (?[[:alpha:]]{2,3})
12 | (?:_ (?[[:alpha:]]{2,3}) )?
13 | (?:\. (?[^@]+) )?
14 | (?:@ (?.*) )?
15 | ]x
16 | LOCALE_DIR = "/lc/"
17 |
18 | LEGACY_ENCODING_ALIAS_MAP = {
19 | 'ujis' => Encoding::EUC_JP,
20 | 'euc' => Encoding::EUC_JP
21 | }
22 |
23 | @@loaded = []
24 |
25 | def initialize(locale = nil)
26 | @override_encoding = nil
27 | @lang = @territory = @encoding_name = @modifier = nil
28 | @locale = locale || ENV["IRB_LANG"] || ENV["LC_MESSAGES"] || ENV["LC_ALL"] || ENV["LANG"] || "C"
29 | if m = LOCALE_NAME_RE.match(@locale)
30 | @lang, @territory, @encoding_name, @modifier = m[:language], m[:territory], m[:codeset], m[:modifier]
31 |
32 | if @encoding_name
33 | if @encoding = LEGACY_ENCODING_ALIAS_MAP[@encoding_name]
34 | warn(("%s is obsolete. use %s" % ["#{@lang}_#{@territory}.#{@encoding_name}", "#{@lang}_#{@territory}.#{@encoding.name}"]), uplevel: 1)
35 | else
36 | @encoding = Encoding.find(@encoding_name) rescue nil
37 | end
38 | end
39 | end
40 | @encoding ||= (Encoding.find('locale') rescue Encoding::ASCII_8BIT)
41 | end
42 |
43 | attr_reader :lang, :territory, :modifier
44 |
45 | def encoding
46 | @override_encoding || @encoding
47 | end
48 |
49 | def String(mes)
50 | mes = super(mes)
51 | if encoding
52 | mes.encode(encoding, undef: :replace)
53 | else
54 | mes
55 | end
56 | end
57 |
58 | def format(*opts)
59 | String(super(*opts))
60 | end
61 |
62 | def gets(*rs)
63 | String(super(*rs))
64 | end
65 |
66 | def readline(*rs)
67 | String(super(*rs))
68 | end
69 |
70 | def print(*opts)
71 | ary = opts.collect{|opt| String(opt)}
72 | super(*ary)
73 | end
74 |
75 | def printf(*opts)
76 | s = format(*opts)
77 | print s
78 | end
79 |
80 | def puts(*opts)
81 | ary = opts.collect{|opt| String(opt)}
82 | super(*ary)
83 | end
84 |
85 | def load(file)
86 | found = find(file)
87 | if found
88 | unless @@loaded.include?(found)
89 | @@loaded << found # cache
90 | Kernel.load(found)
91 | end
92 | else
93 | raise LoadError, "No such file to load -- #{file}"
94 | end
95 | end
96 |
97 | def find(file, paths = $:)
98 | dir = File.dirname(file)
99 | dir = "" if dir == "."
100 | base = File.basename(file)
101 |
102 | if dir.start_with?('/')
103 | return each_localized_path(dir, base).find{|full_path| File.readable? full_path}
104 | else
105 | return search_file(paths, dir, base)
106 | end
107 | end
108 |
109 | # @param paths load paths in which IRB find a localized file.
110 | # @param dir directory
111 | # @param file basename to be localized
112 | #
113 | # typically, for the parameters and a in paths, it searches
114 | # ///
115 | def search_file(lib_paths, dir, file)
116 | each_localized_path(dir, file) do |lc_path|
117 | lib_paths.each do |libpath|
118 | full_path = File.join(libpath, lc_path)
119 | return full_path if File.readable?(full_path)
120 | end
121 | redo if defined?(Gem) and Gem.try_activate(lc_path)
122 | end
123 | nil
124 | end
125 |
126 | def each_localized_path(dir, file)
127 | return enum_for(:each_localized_path) unless block_given?
128 | each_sublocale do |lc|
129 | yield lc.nil? ? File.join(dir, LOCALE_DIR, file) : File.join(dir, LOCALE_DIR, lc, file)
130 | end
131 | end
132 |
133 | def each_sublocale
134 | if @lang
135 | if @territory
136 | if @encoding_name
137 | yield "#{@lang}_#{@territory}.#{@encoding_name}@#{@modifier}" if @modifier
138 | yield "#{@lang}_#{@territory}.#{@encoding_name}"
139 | end
140 | yield "#{@lang}_#{@territory}@#{@modifier}" if @modifier
141 | yield "#{@lang}_#{@territory}"
142 | end
143 | if @encoding_name
144 | yield "#{@lang}.#{@encoding_name}@#{@modifier}" if @modifier
145 | yield "#{@lang}.#{@encoding_name}"
146 | end
147 | yield "#{@lang}@#{@modifier}" if @modifier
148 | yield "#{@lang}"
149 | end
150 | yield nil
151 | end
152 | end
153 | end
154 |
--------------------------------------------------------------------------------
/lib/irb/output-method.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # output-method.rb - output methods used by irb
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | module IRB
8 | # An abstract output class for IO in irb. This is mainly used internally by
9 | # IRB::Notifier. You can define your own output method to use with Irb.new,
10 | # or Context.new
11 | class OutputMethod
12 | # Open this method to implement your own output method, raises a
13 | # NotImplementedError if you don't define #print in your own class.
14 | def print(*opts)
15 | raise NotImplementedError
16 | end
17 |
18 | # Prints the given +opts+, with a newline delimiter.
19 | def printn(*opts)
20 | print opts.join(" "), "\n"
21 | end
22 |
23 | # Extends IO#printf to format the given +opts+ for Kernel#sprintf using
24 | # #parse_printf_format
25 | def printf(format, *opts)
26 | if /(%*)%I/ =~ format
27 | format, opts = parse_printf_format(format, opts)
28 | end
29 | print sprintf(format, *opts)
30 | end
31 |
32 | # Returns an array of the given +format+ and +opts+ to be used by
33 | # Kernel#sprintf, if there was a successful Regexp match in the given
34 | # +format+ from #printf
35 | #
36 | # %
37 | # [#0- +]
38 | # (\*|\*[1-9][0-9]*\$|[1-9][0-9]*)
39 | # .(\*|\*[1-9][0-9]*\$|[1-9][0-9]*|)?
40 | # #(hh|h|l|ll|L|q|j|z|t)
41 | # [diouxXeEfgGcsb%]
42 | def parse_printf_format(format, opts)
43 | return format, opts if $1.size % 2 == 1
44 | end
45 |
46 | # Calls #print on each element in the given +objs+, followed by a newline
47 | # character.
48 | def puts(*objs)
49 | for obj in objs
50 | print(*obj)
51 | print "\n"
52 | end
53 | end
54 |
55 | # Prints the given +objs+ calling Object#inspect on each.
56 | #
57 | # See #puts for more detail.
58 | def pp(*objs)
59 | puts(*objs.collect{|obj| obj.inspect})
60 | end
61 |
62 | # Prints the given +objs+ calling Object#inspect on each and appending the
63 | # given +prefix+.
64 | #
65 | # See #puts for more detail.
66 | def ppx(prefix, *objs)
67 | puts(*objs.collect{|obj| prefix+obj.inspect})
68 | end
69 |
70 | end
71 |
72 | # A standard output printer
73 | class StdioOutputMethod < OutputMethod
74 | # Prints the given +opts+ to standard output, see IO#print for more
75 | # information.
76 | def print(*opts)
77 | STDOUT.print(*opts)
78 | end
79 | end
80 | end
81 |
--------------------------------------------------------------------------------
/lib/irb/source_finder.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require_relative "ruby-lex"
4 |
5 | module IRB
6 | class SourceFinder
7 | class EvaluationError < StandardError; end
8 |
9 | class Source
10 | attr_reader :file, :line
11 | def initialize(file, line, ast_source = nil)
12 | @file = file
13 | @line = line
14 | @ast_source = ast_source
15 | end
16 |
17 | def file_exist?
18 | File.exist?(@file)
19 | end
20 |
21 | def binary_file?
22 | # If the line is zero, it means that the target's source is probably in a binary file.
23 | @line.zero?
24 | end
25 |
26 | def file_content
27 | @file_content ||= File.read(@file)
28 | end
29 |
30 | def colorized_content
31 | if !binary_file? && file_exist?
32 | end_line = find_end
33 | # To correctly colorize, we need to colorize full content and extract the relevant lines.
34 | colored = IRB::Color.colorize_code(file_content)
35 | colored.lines[@line - 1...end_line].join
36 | elsif @ast_source
37 | IRB::Color.colorize_code(@ast_source)
38 | end
39 | end
40 |
41 | private
42 |
43 | def find_end
44 | lex = RubyLex.new
45 | code = file_content
46 | lines = code.lines[(@line - 1)..-1]
47 | tokens = RubyLex.ripper_lex_without_warning(lines.join)
48 | prev_tokens = []
49 |
50 | # chunk with line number
51 | tokens.chunk { |tok| tok.pos[0] }.each do |lnum, chunk|
52 | code = lines[0..lnum].join
53 | prev_tokens.concat chunk
54 | continue = lex.should_continue?(prev_tokens)
55 | syntax = lex.check_code_syntax(code, local_variables: [])
56 | if !continue && syntax == :valid
57 | return @line + lnum
58 | end
59 | end
60 | @line
61 | end
62 | end
63 |
64 | private_constant :Source
65 |
66 | def initialize(irb_context)
67 | @irb_context = irb_context
68 | end
69 |
70 | def find_source(signature, super_level = 0)
71 | case signature
72 | when /\A(::)?[A-Z]\w*(::[A-Z]\w*)*\z/ # ConstName, ::ConstName, ConstPath::ConstName
73 | eval_receiver_or_owner(signature) # trigger autoload
74 | *parts, name = signature.split('::', -1)
75 | base =
76 | if parts.empty? # ConstName
77 | find_const_owner(name)
78 | elsif parts == [''] # ::ConstName
79 | Object
80 | else # ConstPath::ConstName
81 | eval_receiver_or_owner(parts.join('::'))
82 | end
83 | file, line = base.const_source_location(name)
84 | when /\A(?[A-Z]\w*(::[A-Z]\w*)*)#(?[^ :.]+)\z/ # Class#method
85 | owner = eval_receiver_or_owner(Regexp.last_match[:owner])
86 | method = Regexp.last_match[:method]
87 | return unless owner.respond_to?(:instance_method)
88 | method = method_target(owner, super_level, method, "owner")
89 | file, line = method&.source_location
90 | when /\A((?.+)(\.|::))?(?[^ :.]+)\z/ # method, receiver.method, receiver::method
91 | receiver = eval_receiver_or_owner(Regexp.last_match[:receiver] || 'self')
92 | method = Regexp.last_match[:method]
93 | return unless receiver.respond_to?(method, true)
94 | method = method_target(receiver, super_level, method, "receiver")
95 | file, line = method&.source_location
96 | end
97 | return unless file && line
98 |
99 | if File.exist?(file)
100 | Source.new(file, line)
101 | elsif method
102 | # Method defined with eval, probably in IRB session
103 | source = RubyVM::InstructionSequence.of(method)&.script_lines&.join rescue nil
104 | Source.new(file, line, source)
105 | end
106 | rescue EvaluationError
107 | nil
108 | end
109 |
110 | private
111 |
112 | def method_target(owner_receiver, super_level, method, type)
113 | case type
114 | when "owner"
115 | target_method = owner_receiver.instance_method(method)
116 | when "receiver"
117 | target_method = owner_receiver.method(method)
118 | end
119 | super_level.times do |s|
120 | target_method = target_method.super_method if target_method
121 | end
122 | target_method
123 | rescue NameError
124 | nil
125 | end
126 |
127 | def eval_receiver_or_owner(code)
128 | @irb_context.workspace.binding.eval(code)
129 | rescue Exception
130 | raise EvaluationError
131 | end
132 |
133 | def find_const_owner(name)
134 | module_nesting = @irb_context.workspace.binding.eval('::Module.nesting')
135 | module_nesting.find { |mod| mod.const_defined?(name, false) } || module_nesting.find { |mod| mod.const_defined?(name) } || Object
136 | end
137 | end
138 | end
139 |
--------------------------------------------------------------------------------
/lib/irb/statement.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | module IRB
4 | class Statement
5 | attr_reader :code
6 |
7 | def is_assignment?
8 | raise NotImplementedError
9 | end
10 |
11 | def suppresses_echo?
12 | raise NotImplementedError
13 | end
14 |
15 | def should_be_handled_by_debugger?
16 | raise NotImplementedError
17 | end
18 |
19 | class EmptyInput < Statement
20 | def is_assignment?
21 | false
22 | end
23 |
24 | def suppresses_echo?
25 | true
26 | end
27 |
28 | # Debugger takes empty input to repeat the last command
29 | def should_be_handled_by_debugger?
30 | true
31 | end
32 |
33 | def code
34 | ""
35 | end
36 | end
37 |
38 | class Expression < Statement
39 | def initialize(code, is_assignment)
40 | @code = code
41 | @is_assignment = is_assignment
42 | end
43 |
44 | def suppresses_echo?
45 | @code.match?(/;\s*\z/)
46 | end
47 |
48 | def should_be_handled_by_debugger?
49 | true
50 | end
51 |
52 | def is_assignment?
53 | @is_assignment
54 | end
55 | end
56 |
57 | class IncorrectAlias < Statement
58 | attr_reader :message
59 |
60 | def initialize(message)
61 | @code = ""
62 | @message = message
63 | end
64 |
65 | def should_be_handled_by_debugger?
66 | false
67 | end
68 |
69 | def is_assignment?
70 | false
71 | end
72 |
73 | def suppresses_echo?
74 | true
75 | end
76 | end
77 |
78 | class Command < Statement
79 | attr_reader :command_class, :arg
80 |
81 | def initialize(original_code, command_class, arg)
82 | @code = original_code
83 | @command_class = command_class
84 | @arg = arg
85 | end
86 |
87 | def is_assignment?
88 | false
89 | end
90 |
91 | def suppresses_echo?
92 | true
93 | end
94 |
95 | def should_be_handled_by_debugger?
96 | require_relative 'command/debug'
97 | IRB::Command::DebugCommand > @command_class
98 | end
99 | end
100 | end
101 | end
102 |
--------------------------------------------------------------------------------
/lib/irb/version.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # irb/version.rb - irb version definition file
4 | # by Keiju ISHITSUKA(keiju@ishitsuka.com)
5 | #
6 |
7 | module IRB # :nodoc:
8 | VERSION = "1.15.2"
9 | @RELEASE_VERSION = VERSION
10 | @LAST_UPDATE_DATE = "2025-04-03"
11 | end
12 |
--------------------------------------------------------------------------------
/lib/irb/workspace.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # irb/workspace-binding.rb -
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | require_relative "helper_method"
8 |
9 | IRB::TOPLEVEL_BINDING = binding
10 | module IRB # :nodoc:
11 | class WorkSpace
12 | # Creates a new workspace.
13 | #
14 | # set self to main if specified, otherwise
15 | # inherit main from TOPLEVEL_BINDING.
16 | def initialize(*main)
17 | if Binding === main[0]
18 | @binding = main.shift
19 | elsif IRB.conf[:SINGLE_IRB]
20 | @binding = TOPLEVEL_BINDING
21 | else
22 | case IRB.conf[:CONTEXT_MODE]
23 | when 0 # binding in proc on TOPLEVEL_BINDING
24 | @binding = eval("proc{binding}.call",
25 | TOPLEVEL_BINDING,
26 | __FILE__,
27 | __LINE__)
28 | when 1 # binding in loaded file
29 | require "tempfile"
30 | f = Tempfile.open("irb-binding")
31 | f.print <IRB.conf[:__MAIN__]
88 | attr_reader :main
89 |
90 | def load_helper_methods_to_main
91 | # Do not load helper methods to frozen objects and BasicObject
92 | return unless Object === @main && !@main.frozen?
93 |
94 | ancestors = class<' : '', current_pos + 1, lines[current_pos])
152 | end.join("")
153 |
154 | "\nFrom: #{file} @ line #{pos + 1} :\n\n#{body}#{Color.clear}\n"
155 | end
156 | end
157 |
158 | module HelpersContainer
159 | class << self
160 | def install_helper_methods
161 | HelperMethod.helper_methods.each do |name, helper_method_class|
162 | define_method name do |*args, **opts, &block|
163 | helper_method_class.instance.execute(*args, **opts, &block)
164 | end unless method_defined?(name)
165 | end
166 | end
167 | end
168 |
169 | install_helper_methods
170 | end
171 | end
172 |
--------------------------------------------------------------------------------
/lib/irb/ws-for-case-2.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # irb/ws-for-case-2.rb -
4 | # by Keiju ISHITSUKA(keiju@ruby-lang.org)
5 | #
6 |
7 | while true
8 | IRB::BINDING_QUEUE.push _ = binding
9 | end
10 |
--------------------------------------------------------------------------------
/lib/irb/xmp.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | #
3 | # xmp.rb - irb version of gotoken xmp
4 | # by Keiju ISHITSUKA(Nippon Rational Inc.)
5 | #
6 |
7 | require_relative "../irb"
8 | require_relative "frame"
9 |
10 | # An example printer for irb.
11 | #
12 | # It's much like the standard library PrettyPrint, that shows the value of each
13 | # expression as it runs.
14 | #
15 | # In order to use this library, you must first require it:
16 | #
17 | # require 'irb/xmp'
18 | #
19 | # Now, you can take advantage of the Object#xmp convenience method.
20 | #
21 | # xmp < foo = "bar"
26 | # #==>"bar"
27 | # #=> baz = 42
28 | # #==>42
29 | #
30 | # You can also create an XMP object, with an optional binding to print
31 | # expressions in the given binding:
32 | #
33 | # ctx = binding
34 | # x = XMP.new ctx
35 | # x.puts
36 | # #=> today = "a good day"
37 | # #==>"a good day"
38 | # ctx.eval 'today # is what?'
39 | # #=> "a good day"
40 | class XMP
41 |
42 | # Creates a new XMP object.
43 | #
44 | # The top-level binding or, optional +bind+ parameter will be used when
45 | # creating the workspace. See WorkSpace.new for more information.
46 | #
47 | # This uses the +:XMP+ prompt mode.
48 | # See {Custom Prompts}[rdoc-ref:IRB@Custom+Prompts] for more information.
49 | def initialize(bind = nil)
50 | IRB.init_config(nil)
51 |
52 | IRB.conf[:PROMPT_MODE] = :XMP
53 |
54 | bind = IRB::Frame.top(1) unless bind
55 | ws = IRB::WorkSpace.new(bind)
56 | @io = StringInputMethod.new
57 | @irb = IRB::Irb.new(ws, @io)
58 | @irb.context.ignore_sigint = false
59 |
60 | IRB.conf[:MAIN_CONTEXT] = @irb.context
61 | end
62 |
63 | # Evaluates the given +exps+, for example:
64 | #
65 | # require 'irb/xmp'
66 | # x = XMP.new
67 | #
68 | # x.puts '{:a => 1, :b => 2, :c => 3}'
69 | # #=> {:a => 1, :b => 2, :c => 3}
70 | # # ==>{:a=>1, :b=>2, :c=>3}
71 | # x.puts 'foo = "bar"'
72 | # # => foo = "bar"
73 | # # ==>"bar"
74 | def puts(exps)
75 | @io.puts exps
76 |
77 | if @irb.context.ignore_sigint
78 | begin
79 | trap_proc_b = trap("SIGINT"){@irb.signal_handle}
80 | catch(:IRB_EXIT) do
81 | @irb.eval_input
82 | end
83 | ensure
84 | trap("SIGINT", trap_proc_b)
85 | end
86 | else
87 | catch(:IRB_EXIT) do
88 | @irb.eval_input
89 | end
90 | end
91 | end
92 |
93 | # A custom InputMethod class used by XMP for evaluating string io.
94 | class StringInputMethod < IRB::InputMethod
95 | # Creates a new StringInputMethod object
96 | def initialize
97 | super
98 | @exps = []
99 | end
100 |
101 | # Whether there are any expressions left in this printer.
102 | def eof?
103 | @exps.empty?
104 | end
105 |
106 | # Reads the next expression from this printer.
107 | #
108 | # See IO#gets for more information.
109 | def gets
110 | while l = @exps.shift
111 | next if /^\s+$/ =~ l
112 | l.concat "\n"
113 | print @prompt, l
114 | break
115 | end
116 | l
117 | end
118 |
119 | # Concatenates all expressions in this printer, separated by newlines.
120 | #
121 | # An Encoding::CompatibilityError is raised of the given +exps+'s encoding
122 | # doesn't match the previous expression evaluated.
123 | def puts(exps)
124 | if @encoding and exps.encoding != @encoding
125 | enc = Encoding.compatible?(@exps.join("\n"), exps)
126 | if enc.nil?
127 | raise Encoding::CompatibilityError, "Encoding in which the passed expression is encoded is not compatible to the preceding's one"
128 | else
129 | @encoding = enc
130 | end
131 | else
132 | @encoding = exps.encoding
133 | end
134 | @exps.concat exps.split(/\n/)
135 | end
136 |
137 | # Returns the encoding of last expression printed by #puts.
138 | attr_reader :encoding
139 | end
140 | end
141 |
142 | # A convenience method that's only available when the you require the IRB::XMP standard library.
143 | #
144 | # Creates a new XMP object, using the given expressions as the +exps+
145 | # parameter, and optional binding as +bind+ or uses the top-level binding. Then
146 | # evaluates the given expressions using the +:XMP+ prompt mode.
147 | #
148 | # For example:
149 | #
150 | # require 'irb/xmp'
151 | # ctx = binding
152 | # xmp 'foo = "bar"', ctx
153 | # #=> foo = "bar"
154 | # #==>"bar"
155 | # ctx.eval 'foo'
156 | # #=> "bar"
157 | #
158 | # See XMP.new for more information.
159 | def xmp(exps, bind = nil)
160 | bind = IRB::Frame.top(1) unless bind
161 | xmp = XMP.new(bind)
162 | xmp.puts exps
163 | xmp
164 | end
165 |
--------------------------------------------------------------------------------
/man/irb.1:
--------------------------------------------------------------------------------
1 | .\"Ruby is copyrighted by Yukihiro Matsumoto .
2 | .Dd August 11, 2019
3 | .Dt IRB \&1 "Ruby Programmer's Reference Guide"
4 | .Os UNIX
5 | .Sh NAME
6 | .Nm irb
7 | .Nd Interactive Ruby Shell
8 | .Sh SYNOPSIS
9 | .Nm
10 | .Op Fl -version
11 | .Op Fl dfUw
12 | .Op Fl I Ar directory
13 | .Op Fl r Ar library
14 | .Op Fl E Ar external Ns Op : Ns Ar internal
15 | .Op Fl W Ns Op Ar level
16 | .Op Fl - Ns Oo no Oc Ns inspect
17 | .Op Fl - Ns Oo no Oc Ns multiline
18 | .Op Fl - Ns Oo no Oc Ns singleline
19 | .Op Fl - Ns Oo no Oc Ns echo
20 | .Op Fl - Ns Oo no Oc Ns colorize
21 | .Op Fl - Ns Oo no Oc Ns autocomplete
22 | .Op Fl - Ns Oo no Oc Ns verbose
23 | .Op Fl -prompt Ar mode
24 | .Op Fl -prompt-mode Ar mode
25 | .Op Fl -inf-ruby-mode
26 | .Op Fl -simple-prompt
27 | .Op Fl -noprompt
28 | .Op Fl -tracer
29 | .Op Fl -back-trace-limit Ar n
30 | .Op Fl -
31 | .Op program_file
32 | .Op argument ...
33 | .Pp
34 | .Sh DESCRIPTION
35 | .Nm
36 | is the REPL(read-eval-print loop) environment for Ruby programs.
37 | .Pp
38 | .Sh OPTIONS
39 | .Bl -tag -width "1234567890123" -compact
40 | .Pp
41 | .It Fl -version
42 | Prints the version of
43 | .Nm .
44 | .Pp
45 | .It Fl E Ar external Ns Op : Ns Ar internal
46 | .It Fl -encoding Ar external Ns Op : Ns Ar internal
47 | Same as `ruby -E' .
48 | Specifies the default value(s) for external encodings and internal encoding. Values should be separated with colon (:).
49 | .Pp
50 | You can omit the one for internal encodings, then the value
51 | .Pf ( Li "Encoding.default_internal" ) will be nil.
52 | .Pp
53 | .It Fl I Ar path
54 | Same as `ruby -I' .
55 | Specifies
56 | .Li $LOAD_PATH
57 | directory
58 | .Pp
59 | .It Fl U
60 | Same as `ruby -U' .
61 | Sets the default value for internal encodings
62 | .Pf ( Li "Encoding.default_internal" ) to UTF-8.
63 | .Pp
64 | .It Fl d
65 | Same as `ruby -d' .
66 | Sets
67 | .Li $DEBUG
68 | to true.
69 | .Pp
70 | .It Fl f
71 | Suppresses read of
72 | .Pa ~/.irbrc .
73 | .Pp
74 | .It Fl w
75 | Same as `ruby -w' .
76 | .Pp
77 | .Pp
78 | .It Fl W
79 | Same as `ruby -W' .
80 | .Pp
81 | .It Fl h
82 | .It Fl -help
83 | Prints a summary of the options.
84 | .Pp
85 | .It Fl r Ar library
86 | Same as `ruby -r'.
87 | Causes irb to load the library using require.
88 | .Pp
89 | .It Fl -inspect
90 | Uses `inspect' for output (default except for bc mode)
91 | .Pp
92 | .It Fl -noinspect
93 | Doesn't use inspect for output
94 | .Pp
95 | .It Fl -multiline
96 | Uses multiline editor module.
97 | .Pp
98 | .It Fl -nomultiline
99 | Doesn't use multiline editor module.
100 | .Pp
101 | .It Fl -singleline
102 | Uses singleline editor module.
103 | .Pp
104 | .It Fl -nosingleline
105 | Doesn't use singleline editor module.
106 | .Pp
107 | .Pp
108 | .It Fl -extra-doc-dir
109 | Add an extra doc dir for the doc dialog.
110 | .Pp
111 | .Pp
112 | .It Fl -echo
113 | Show result (default).
114 | .Pp
115 | .It Fl -noecho
116 | Don't show result.
117 | .Pp
118 | .Pp
119 | .It Fl -echo-on-assignment
120 | Show result on assignment.
121 | .Pp
122 | .It Fl -noecho-on-assignment
123 | Don't show result on assignment.
124 | .Pp
125 | .It Fl -truncate-echo-on-assignment
126 | Show truncated result on assignment (default).
127 | .Pp
128 | .Pp
129 | .It Fl -colorize
130 | Use colorization.
131 | .Pp
132 | .It Fl -nocolorize
133 | Don't use colorization.
134 | .Pp
135 | .Pp
136 | .It Fl -autocomplete
137 | Use autocompletion.
138 | .Pp
139 | .It Fl -noautocomplete
140 | Don't use autocompletion.
141 | .Pp
142 | .Pp
143 | .It Fl -regexp-completor
144 | Use regexp based completion.
145 | .Pp
146 | .It Fl -type-completor
147 | Use type based completion.
148 | .Pp
149 | .Pp
150 | .It Fl -verbose
151 | Show details.
152 | .Pp
153 | .It Fl -noverbose
154 | Don't show details.
155 | .Pp
156 | .It Fl -prompt Ar mode
157 | .It Fl -prompt-mode Ar mode
158 | Switch prompt mode. Pre-defined prompt modes are
159 | `default', `simple', `xmp' and `inf-ruby'.
160 | .Pp
161 | .It Fl -inf-ruby-mode
162 | Uses prompt appropriate for inf-ruby-mode on emacs.
163 | Suppresses --multiline and --singleline.
164 | .Pp
165 | .It Fl -simple-prompt
166 | Makes prompts simple.
167 | .Pp
168 | .It Fl -noprompt
169 | No prompt mode.
170 | .Pp
171 | .It Fl -tracer
172 | Displays trace for each execution of commands.
173 | .Pp
174 | .It Fl -back-trace-limit Ar n
175 | Displays backtrace top
176 | .Ar n
177 | and tail
178 | .Ar n Ns .
179 | The default value is 16.
180 | .El
181 | .Pp
182 | .Sh ENVIRONMENT
183 | .Bl -tag -compact -width "IRB_USE_AUTOCOMPLETE"
184 | .It Ev IRB_LANG
185 | The locale used for
186 | .Nm .
187 | .Pp
188 | .It Ev IRBRC
189 | The path to the personal initialization file.
190 | .Pp
191 | .It Ev XDG_CONFIG_HOME
192 | .Nm
193 | respects XDG_CONFIG_HOME. If it is set and
194 | .Ev IRBRC
195 | is unset, load
196 | .Pa $XDG_CONFIG_HOME/irb/irbrc
197 | as a personal initialization file.
198 | .Pp
199 | .It Ev RI_PAGER
200 | The command specified would be used as a pager.
201 | .Pp
202 | .It Ev PAGER
203 | The command specified would be used as a pager if
204 | .Ev RI_PAGER
205 | is unset.
206 | .Pp
207 | .It Ev VISUAL
208 | Its value would be used to open files by the edit command.
209 | .Pp
210 | .It Ev EDITOR
211 | Its value would be used to open files by the edit command if
212 | .Ev VISUAL
213 | is unset.
214 | .Pp
215 | .It Ev NO_COLOR
216 | Assigning a value to it disables colorization.
217 | .Pp
218 | .It Ev IRB_USE_AUTOCOMPLETE
219 | Assigning
220 | .Sy false
221 | to it disables autocompletion.
222 | .Pp
223 | .It Ev IRB_COMPLETOR
224 | Autocompletion behavior. Allowed values are
225 | .Sy regexp
226 | or
227 | .Sy type
228 | .
229 | .Pp
230 | .It Ev IRB_COPY_COMMAND
231 | Overrides the default program used to interface with the system clipboard.
232 | .El
233 | .Pp
234 | Also
235 | .Nm
236 | depends on same variables as
237 | .Xr ruby 1 .
238 | .Pp
239 | .Sh FILES
240 | .Bl -tag -compact
241 | .It Pa ~/.irbrc
242 | Personal irb initialization. If
243 | .Ev IRBRC
244 | is set, read
245 | .Pa $IRBRC
246 | instead. If
247 | .Ev IRBRC
248 | is not set and
249 | .Ev XDG_CONFIG_HOME
250 | is set,
251 | .Pa $XDG_CONFIG_HOME/irb/irbrc
252 | is loaded.
253 | .Pp
254 | .El
255 | .Pp
256 | .Sh EXAMPLES
257 | .Dl % irb
258 | .Dl irb(main):001:0> Ic 1 + 1
259 | .Dl 2
260 | .Dl irb(main):002:0> Ic def t(x)
261 | .Dl irb(main):003:1> Ic x + 1
262 | .Dl irb(main):004:1> Ic end
263 | .Dl => :t
264 | .Dl irb(main):005:0> Ic t(3)
265 | .Dl => 4
266 | .Dl irb(main):006:0> Ic if t(3) == 4
267 | .Dl irb(main):007:1> Ic p :ok
268 | .Dl irb(main):008:1> Ic end
269 | .Dl :ok
270 | .Dl => :ok
271 | .Dl irb(main):009:0> Ic quit
272 | .Dl %
273 | .Pp
274 | .Sh SEE ALSO
275 | .Xr ruby 1 .
276 | .Pp
277 | .Sh REPORTING BUGS
278 | .Bl -bullet
279 | .It
280 | Security vulnerabilities should be reported via an email to
281 | .Mt security@ruby-lang.org .
282 | Reported problems will be published after being fixed.
283 | .Pp
284 | .It
285 | Other bugs and feature requests can be reported via the
286 | Ruby Issue Tracking System
287 | .Pq Lk https://bugs.ruby-lang.org/ .
288 | Do not report security vulnerabilities
289 | via this system because it publishes the vulnerabilities immediately.
290 | .El
291 | .Sh AUTHORS
292 | Written by Keiju ISHITSUKA.
293 |
--------------------------------------------------------------------------------
/test/irb/command/test_cd.rb:
--------------------------------------------------------------------------------
1 | require "tempfile"
2 | require_relative "../helper"
3 |
4 | module TestIRB
5 | class CDTest < IntegrationTestCase
6 | def setup
7 | super
8 |
9 | write_ruby <<~'RUBY'
10 | class Foo
11 | class Bar
12 | def bar
13 | "this is bar"
14 | end
15 | end
16 |
17 | def foo
18 | "this is foo"
19 | end
20 | end
21 |
22 | class BO < BasicObject
23 | def baz
24 | "this is baz"
25 | end
26 | end
27 |
28 | binding.irb
29 | RUBY
30 | end
31 |
32 | def test_cd
33 | out = run_ruby_file do
34 | type "cd Foo"
35 | type "ls"
36 | type "cd Bar"
37 | type "ls"
38 | type "cd .."
39 | type "exit"
40 | end
41 |
42 | assert_match(/irb\(Foo\):002>/, out)
43 | assert_match(/Foo#methods: foo/, out)
44 | assert_match(/irb\(Foo::Bar\):004>/, out)
45 | assert_match(/Bar#methods: bar/, out)
46 | assert_match(/irb\(Foo\):006>/, out)
47 | end
48 |
49 | def test_cd_basic_object_or_frozen
50 | out = run_ruby_file do
51 | type "cd BO.new"
52 | type "cd 1"
53 | type "cd Object.new.freeze"
54 | type "exit"
55 | end
56 |
57 | assert_match(/irb\(#/, out)
58 | assert_match(/irb\(1\):003>/, out)
59 | assert_match(/irb\(#/, out)
60 | end
61 |
62 | def test_cd_moves_top_level_with_no_args
63 | out = run_ruby_file do
64 | type "cd Foo"
65 | type "cd Bar"
66 | type "cd"
67 | type "exit"
68 | end
69 |
70 | assert_match(/irb\(Foo::Bar\):003>/, out)
71 | assert_match(/irb\(main\):004>/, out)
72 | end
73 |
74 | def test_cd_with_error
75 | out = run_ruby_file do
76 | type "cd Baz"
77 | type "exit"
78 | end
79 |
80 | assert_match(/Error: uninitialized constant Baz/, out)
81 | assert_match(/irb\(main\):002>/, out) # the context should not change
82 | end
83 | end
84 | end
85 |
--------------------------------------------------------------------------------
/test/irb/command/test_command_aliasing.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "tempfile"
4 | require_relative "../helper"
5 |
6 | module TestIRB
7 | class CommandAliasingTest < IntegrationTestCase
8 | def setup
9 | super
10 | write_rc <<~RUBY
11 | IRB.conf[:COMMAND_ALIASES] = {
12 | :c => :conf, # alias to helper method
13 | :f => :foo
14 | }
15 | RUBY
16 |
17 | write_ruby <<~'RUBY'
18 | binding.irb
19 | RUBY
20 | end
21 |
22 | def test_aliasing_to_helper_method_triggers_warning
23 | out = run_ruby_file do
24 | type "c"
25 | type "exit"
26 | end
27 | assert_include(out, "Using command alias `c` for helper method `conf` is not supported.")
28 | assert_not_include(out, "Maybe IRB bug!")
29 | end
30 |
31 | def test_alias_to_non_existent_command_triggers_warning
32 | message = "You're trying to use command alias `f` for command `foo`, but `foo` does not exist."
33 | out = run_ruby_file do
34 | type "f"
35 | type "exit"
36 | end
37 | assert_include(out, message)
38 | assert_not_include(out, "Maybe IRB bug!")
39 |
40 | # Local variables take precedence over command aliases
41 | out = run_ruby_file do
42 | type "f = 123"
43 | type "f"
44 | type "exit"
45 | end
46 | assert_not_include(out, message)
47 | assert_not_include(out, "Maybe IRB bug!")
48 | end
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/test/irb/command/test_copy.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'irb'
4 |
5 | require_relative "../helper"
6 |
7 | module TestIRB
8 | class CopyTest < IntegrationTestCase
9 | def setup
10 | super
11 | @envs['IRB_COPY_COMMAND'] = "#{EnvUtil.rubybin} -e \"puts 'foo' + STDIN.read\""
12 | end
13 |
14 | def test_copy_with_pbcopy
15 | write_ruby <<~'ruby'
16 | class Answer
17 | def initialize(answer)
18 | @answer = answer
19 | end
20 | end
21 |
22 | binding.irb
23 | ruby
24 |
25 | output = run_ruby_file do
26 | type "copy Answer.new(42)"
27 | type "exit"
28 | end
29 |
30 | assert_match(/foo# "3"')
187 | assert_include(output, '=> "4"')
188 | assert_include(output, '=> "5"')
189 | assert_include(output, '=> "6"')
190 | assert_include(output, '=> "7"')
191 | assert_include(output, '=> "8"')
192 | end
193 | end
194 | end
195 |
--------------------------------------------------------------------------------
/test/irb/command/test_disable_irb.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: false
2 | require 'irb'
3 |
4 | require_relative "../helper"
5 |
6 | module TestIRB
7 | class DisableIRBTest < IntegrationTestCase
8 | def test_disable_irb_disable_further_irb_breakpoints
9 | write_ruby <<~'ruby'
10 | puts "First line"
11 | puts "Second line"
12 | binding.irb
13 | puts "Third line"
14 | binding.irb
15 | puts "Fourth line"
16 | ruby
17 |
18 | output = run_ruby_file do
19 | type "disable_irb"
20 | end
21 |
22 | assert_match(/First line\r\n/, output)
23 | assert_match(/Second line\r\n/, output)
24 | assert_match(/Third line\r\n/, output)
25 | assert_match(/Fourth line\r\n/, output)
26 | end
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/test/irb/command/test_force_exit.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: false
2 | require 'irb'
3 |
4 | require_relative "../helper"
5 |
6 | module TestIRB
7 | class ForceExitTest < IntegrationTestCase
8 | def test_forced_exit_finishes_process_immediately
9 | write_ruby <<~'ruby'
10 | puts "First line"
11 | puts "Second line"
12 | binding.irb
13 | puts "Third line"
14 | binding.irb
15 | puts "Fourth line"
16 | ruby
17 |
18 | output = run_ruby_file do
19 | type "123"
20 | type "456"
21 | type "exit!"
22 | end
23 |
24 | assert_match(/First line\r\n/, output)
25 | assert_match(/Second line\r\n/, output)
26 | assert_match(/irb\(main\):001> 123/, output)
27 | assert_match(/irb\(main\):002> 456/, output)
28 | refute_match(/Third line\r\n/, output)
29 | refute_match(/Fourth line\r\n/, output)
30 | end
31 |
32 | def test_forced_exit_in_nested_sessions
33 | write_ruby <<~'ruby'
34 | def foo
35 | binding.irb
36 | end
37 |
38 | binding.irb
39 | binding.irb
40 | ruby
41 |
42 | output = run_ruby_file do
43 | type "123"
44 | type "foo"
45 | type "exit!"
46 | end
47 |
48 | assert_match(/irb\(main\):001> 123/, output)
49 | end
50 | end
51 | end
52 |
--------------------------------------------------------------------------------
/test/irb/command/test_help.rb:
--------------------------------------------------------------------------------
1 | require "tempfile"
2 | require_relative "../helper"
3 |
4 | module TestIRB
5 | class HelpTest < IntegrationTestCase
6 | def setup
7 | super
8 |
9 | write_rc <<~'RUBY'
10 | IRB.conf[:USE_PAGER] = false
11 | RUBY
12 |
13 | write_ruby <<~'RUBY'
14 | binding.irb
15 | RUBY
16 | end
17 |
18 | def test_help
19 | out = run_ruby_file do
20 | type "help"
21 | type "exit"
22 | end
23 |
24 | assert_match(/List all available commands/, out)
25 | assert_match(/Start the debugger of debug\.gem/, out)
26 | end
27 |
28 | def test_command_help
29 | out = run_ruby_file do
30 | type "help ls"
31 | type "exit"
32 | end
33 |
34 | assert_match(/Usage: ls \[obj\]/, out)
35 | end
36 |
37 | def test_command_help_not_found
38 | out = run_ruby_file do
39 | type "help foo"
40 | type "exit"
41 | end
42 |
43 | assert_match(/Can't find command `foo`\. Please check the command name and try again\./, out)
44 | end
45 |
46 | def test_show_cmds
47 | out = run_ruby_file do
48 | type "help"
49 | type "exit"
50 | end
51 |
52 | assert_match(/List all available commands/, out)
53 | assert_match(/Start the debugger of debug\.gem/, out)
54 | end
55 |
56 | def test_help_lists_user_aliases
57 | out = run_ruby_file do
58 | type "help"
59 | type "exit"
60 | end
61 |
62 | assert_match(/\$\s+Alias for `show_source`/, out)
63 | assert_match(/@\s+Alias for `whereami`/, out)
64 | end
65 |
66 | def test_help_lists_helper_methods
67 | out = run_ruby_file do
68 | type "help"
69 | type "exit"
70 | end
71 |
72 | assert_match(/Helper methods\s+conf\s+Returns the current IRB context/, out)
73 | end
74 | end
75 | end
76 |
--------------------------------------------------------------------------------
/test/irb/command/test_multi_irb_commands.rb:
--------------------------------------------------------------------------------
1 | require "tempfile"
2 | require_relative "../helper"
3 |
4 | module TestIRB
5 | class MultiIRBTest < IntegrationTestCase
6 | def setup
7 | super
8 |
9 | write_ruby <<~'RUBY'
10 | binding.irb
11 | RUBY
12 | end
13 |
14 | def test_jobs_command_with_print_deprecated_warning
15 | out = run_ruby_file do
16 | type "jobs"
17 | type "exit"
18 | end
19 |
20 | assert_match(/Multi-irb commands are deprecated and will be removed in IRB 2\.0\.0\. Please use workspace commands instead\./, out)
21 | assert_match(%r|If you have any use case for multi-irb, please leave a comment at https://github.com/ruby/irb/issues/653|, out)
22 | assert_match(/#0->irb on main \(#: running\)/, out)
23 | end
24 |
25 | def test_irb_jobs_and_kill_commands
26 | out = run_ruby_file do
27 | type "irb"
28 | type "jobs"
29 | type "kill 1"
30 | type "exit"
31 | end
32 |
33 | assert_match(/#0->irb on main \(#: stop\)/, out)
34 | assert_match(/#1->irb#1 on main \(#: running\)/, out)
35 | end
36 |
37 | def test_irb_fg_jobs_and_kill_commands
38 | out = run_ruby_file do
39 | type "irb"
40 | type "fg 0"
41 | type "jobs"
42 | type "kill 1"
43 | type "exit"
44 | end
45 |
46 | assert_match(/#0->irb on main \(#: running\)/, out)
47 | assert_match(/#1->irb#1 on main \(#: stop\)/, out)
48 | end
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/test/irb/test_color_printer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: false
2 | require 'irb/color_printer'
3 | require 'stringio'
4 |
5 | require_relative "helper"
6 |
7 | module TestIRB
8 | class ColorPrinterTest < TestCase
9 | CLEAR = "\e[0m"
10 | BOLD = "\e[1m"
11 | RED = "\e[31m"
12 | GREEN = "\e[32m"
13 | BLUE = "\e[34m"
14 | CYAN = "\e[36m"
15 |
16 | def setup
17 | super
18 | if IRB.respond_to?(:conf)
19 | @colorize, IRB.conf[:USE_COLORIZE] = IRB.conf[:USE_COLORIZE], true
20 | end
21 | @get_screen_size = Reline.method(:get_screen_size)
22 | Reline.instance_eval { undef :get_screen_size }
23 | def Reline.get_screen_size
24 | [36, 80]
25 | end
26 | end
27 |
28 | def teardown
29 | Reline.instance_eval { undef :get_screen_size }
30 | Reline.define_singleton_method(:get_screen_size, @get_screen_size)
31 | if instance_variable_defined?(:@colorize)
32 | IRB.conf[:USE_COLORIZE] = @colorize
33 | end
34 | super
35 | end
36 |
37 | IRBTestColorPrinter = Struct.new(:a)
38 |
39 | def test_color_printer
40 | {
41 | 1 => "#{BLUE}#{BOLD}1#{CLEAR}\n",
42 | "a\nb" => %[#{RED}#{BOLD}"#{CLEAR}#{RED}a\\nb#{CLEAR}#{RED}#{BOLD}"#{CLEAR}\n],
43 | IRBTestColorPrinter.new('test') => "#{GREEN}##{CLEAR}\n",
44 | Ripper::Lexer.new('1').scan => "[#{GREEN}##{CLEAR}]\n",
45 | Class.new{define_method(:pretty_print){|q| q.text("[__FILE__, __LINE__, __ENCODING__]")}}.new => "[#{CYAN}#{BOLD}__FILE__#{CLEAR}, #{CYAN}#{BOLD}__LINE__#{CLEAR}, #{CYAN}#{BOLD}__ENCODING__#{CLEAR}]\n",
46 | }.each do |object, result|
47 | actual = with_term { IRB::ColorPrinter.pp(object, '') }
48 | assert_equal(result, actual, "Case: IRB::ColorPrinter.pp(#{object.inspect}, '')")
49 | end
50 | end
51 |
52 | def test_colorization_disabled
53 | {
54 | 1 => "1\n",
55 | "a\nb" => %["a\\nb"\n],
56 | IRBTestColorPrinter.new('test') => "#\n",
57 | Ripper::Lexer.new('1').scan => "[#]\n",
58 | Class.new{define_method(:pretty_print){|q| q.text("[__FILE__, __LINE__, __ENCODING__]")}}.new => "[__FILE__, __LINE__, __ENCODING__]\n",
59 | }.each do |object, result|
60 | actual = with_term { IRB::ColorPrinter.pp(object, '', colorize: false) }
61 | assert_equal(result, actual, "Case: IRB::ColorPrinter.pp(#{object.inspect}, '')")
62 | end
63 | end
64 |
65 | private
66 |
67 | def with_term
68 | stdout = $stdout
69 | io = StringIO.new
70 | def io.tty?; true; end
71 | $stdout = io
72 |
73 | env = ENV.to_h.dup
74 | ENV['TERM'] = 'xterm-256color'
75 |
76 | yield
77 | ensure
78 | $stdout = stdout
79 | ENV.replace(env) if env
80 | end
81 | end
82 | end
83 |
--------------------------------------------------------------------------------
/test/irb/test_eval_history.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require "irb"
3 |
4 | require_relative "helper"
5 |
6 | module TestIRB
7 | class EvalHistoryTest < TestCase
8 | def setup
9 | save_encodings
10 | IRB.instance_variable_get(:@CONF).clear
11 | end
12 |
13 | def teardown
14 | restore_encodings
15 | end
16 |
17 | def test_eval_history_is_disabled_by_default
18 | out, err = execute_lines(
19 | "a = 1",
20 | "__"
21 | )
22 |
23 | assert_empty(err)
24 | assert_match(/undefined local variable or method (`|')__'/, out)
25 | end
26 |
27 | def test_eval_history_can_be_retrieved_with_double_underscore
28 | out, err = execute_lines(
29 | "a = 1",
30 | "__",
31 | conf: { EVAL_HISTORY: 5 }
32 | )
33 |
34 | assert_empty(err)
35 | assert_match("=> 1\n" + "=> 1 1\n", out)
36 | end
37 |
38 | def test_eval_history_respects_given_limit
39 | out, err = execute_lines(
40 | "'foo'\n",
41 | "'bar'\n",
42 | "'baz'\n",
43 | "'xyz'\n",
44 | "__",
45 | conf: { EVAL_HISTORY: 4 }
46 | )
47 |
48 | assert_empty(err)
49 | # Because eval_history injects `__` into the history AND decide to ignore it, we only get - 1 results
50 | assert_match("2 \"bar\"\n" + "3 \"baz\"\n" + "4 \"xyz\"\n", out)
51 | end
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/test/irb/test_evaluation.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require "tempfile"
4 |
5 | require_relative "helper"
6 |
7 | module TestIRB
8 | class EchoingTest < IntegrationTestCase
9 | def test_irb_echos_by_default
10 | write_ruby <<~'RUBY'
11 | binding.irb
12 | RUBY
13 |
14 | output = run_ruby_file do
15 | type "123123"
16 | type "exit"
17 | end
18 |
19 | assert_include(output, "=> 123123")
20 | end
21 |
22 | def test_irb_doesnt_echo_line_with_semicolon
23 | write_ruby <<~'RUBY'
24 | binding.irb
25 | RUBY
26 |
27 | output = run_ruby_file do
28 | type "123123;"
29 | type "123123 ;"
30 | type "123123; "
31 | type <<~RUBY
32 | if true
33 | 123123
34 | end;
35 | RUBY
36 | type "'evaluation ends'"
37 | type "exit"
38 | end
39 |
40 | assert_include(output, "=> \"evaluation ends\"")
41 | assert_not_include(output, "=> 123123")
42 | end
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/test/irb/test_helper_method.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 | require "irb"
3 |
4 | require_relative "helper"
5 |
6 | module TestIRB
7 | class HelperMethodTestCase < TestCase
8 | def setup
9 | $VERBOSE = nil
10 | @verbosity = $VERBOSE
11 | save_encodings
12 | IRB.instance_variable_get(:@CONF).clear
13 | end
14 |
15 | def teardown
16 | $VERBOSE = @verbosity
17 | restore_encodings
18 | end
19 | end
20 |
21 | module TestHelperMethod
22 | class ConfTest < HelperMethodTestCase
23 | def test_conf_returns_the_context_object
24 | out, err = execute_lines("conf.ap_name")
25 |
26 | assert_empty err
27 | assert_include out, "=> \"irb\""
28 | end
29 | end
30 | end
31 |
32 | class HelperMethodIntegrationTest < IntegrationTestCase
33 | def test_arguments_propogation
34 | write_ruby <<~RUBY
35 | require "irb/helper_method"
36 |
37 | class MyHelper < IRB::HelperMethod::Base
38 | description "This is a test helper"
39 |
40 | def execute(
41 | required_arg, optional_arg = nil, *splat_arg, required_keyword_arg:,
42 | optional_keyword_arg: nil, **double_splat_arg, &block_arg
43 | )
44 | puts [required_arg, optional_arg, splat_arg, required_keyword_arg, optional_keyword_arg, double_splat_arg, block_arg.call].to_s
45 | end
46 | end
47 |
48 | IRB::HelperMethod.register(:my_helper, MyHelper)
49 |
50 | binding.irb
51 | RUBY
52 |
53 | output = run_ruby_file do
54 | type <<~INPUT
55 | my_helper(
56 | "required", "optional", "splat", required_keyword_arg: "required",
57 | optional_keyword_arg: "optional", a: 1, b: 2
58 | ) { "block" }
59 | INPUT
60 | type "exit"
61 | end
62 |
63 | optional = {a: 1, b: 2}
64 | assert_include(output, %[["required", "optional", ["splat"], "required", "optional", #{optional.inspect}, "block"]])
65 | end
66 |
67 | def test_helper_method_injection_can_happen_after_irb_require
68 | write_ruby <<~RUBY
69 | require "irb"
70 |
71 | class MyHelper < IRB::HelperMethod::Base
72 | description "This is a test helper"
73 |
74 | def execute
75 | puts "Hello from MyHelper"
76 | end
77 | end
78 |
79 | IRB::HelperMethod.register(:my_helper, MyHelper)
80 |
81 | binding.irb
82 | RUBY
83 |
84 | output = run_ruby_file do
85 | type "my_helper"
86 | type "exit"
87 | end
88 |
89 | assert_include(output, 'Hello from MyHelper')
90 | end
91 |
92 | def test_helper_method_instances_are_memoized
93 | write_ruby <<~RUBY
94 | require "irb/helper_method"
95 |
96 | class MyHelper < IRB::HelperMethod::Base
97 | description "This is a test helper"
98 |
99 | def execute(val)
100 | @val ||= val
101 | end
102 | end
103 |
104 | IRB::HelperMethod.register(:my_helper, MyHelper)
105 |
106 | binding.irb
107 | RUBY
108 |
109 | output = run_ruby_file do
110 | type "my_helper(100)"
111 | type "my_helper(200)"
112 | type "exit"
113 | end
114 |
115 | assert_include(output, '=> 100')
116 | assert_not_include(output, '=> 200')
117 | end
118 | end
119 | end
120 |
--------------------------------------------------------------------------------
/test/irb/test_locale.rb:
--------------------------------------------------------------------------------
1 | require "irb"
2 | require "stringio"
3 |
4 | require_relative "helper"
5 |
6 | module TestIRB
7 | class LocaleTestCase < TestCase
8 | def test_initialize_with_en
9 | locale = IRB::Locale.new("en_US.UTF-8")
10 |
11 | assert_equal("en", locale.lang)
12 | assert_equal("US", locale.territory)
13 | assert_equal("UTF-8", locale.encoding.name)
14 | assert_equal(nil, locale.modifier)
15 | end
16 |
17 | def test_initialize_with_ja
18 | locale = IRB::Locale.new("ja_JP.UTF-8")
19 |
20 | assert_equal("ja", locale.lang)
21 | assert_equal("JP", locale.territory)
22 | assert_equal("UTF-8", locale.encoding.name)
23 | assert_equal(nil, locale.modifier)
24 | end
25 |
26 | def test_initialize_with_legacy_ja_encoding_ujis
27 | original_stderr = $stderr
28 | $stderr = StringIO.new
29 |
30 | locale = IRB::Locale.new("ja_JP.ujis")
31 |
32 | assert_equal("ja", locale.lang)
33 | assert_equal("JP", locale.territory)
34 | assert_equal(Encoding::EUC_JP, locale.encoding)
35 | assert_equal(nil, locale.modifier)
36 |
37 | assert_include $stderr.string, "ja_JP.ujis is obsolete. use ja_JP.EUC-JP"
38 | ensure
39 | $stderr = original_stderr
40 | end
41 |
42 | def test_initialize_with_legacy_ja_encoding_euc
43 | original_stderr = $stderr
44 | $stderr = StringIO.new
45 |
46 | locale = IRB::Locale.new("ja_JP.euc")
47 |
48 | assert_equal("ja", locale.lang)
49 | assert_equal("JP", locale.territory)
50 | assert_equal(Encoding::EUC_JP, locale.encoding)
51 | assert_equal(nil, locale.modifier)
52 |
53 | assert_include $stderr.string, "ja_JP.euc is obsolete. use ja_JP.EUC-JP"
54 | ensure
55 | $stderr = original_stderr
56 | end
57 |
58 | %w(IRB_LANG LC_MESSAGES LC_ALL LANG).each do |env_var|
59 | define_method "test_initialize_with_#{env_var.downcase}" do
60 | original_values = {
61 | "IRB_LANG" => ENV["IRB_LANG"],
62 | "LC_MESSAGES" => ENV["LC_MESSAGES"],
63 | "LC_ALL" => ENV["LC_ALL"],
64 | "LANG" => ENV["LANG"],
65 | }
66 |
67 | ENV["IRB_LANG"] = ENV["LC_MESSAGES"] = ENV["LC_ALL"] = ENV["LANG"] = nil
68 | ENV[env_var] = "zh_TW.UTF-8"
69 |
70 | locale = IRB::Locale.new
71 |
72 | assert_equal("zh", locale.lang)
73 | assert_equal("TW", locale.territory)
74 | assert_equal("UTF-8", locale.encoding.name)
75 | assert_equal(nil, locale.modifier)
76 | ensure
77 | original_values.each do |key, value|
78 | ENV[key] = value
79 | end
80 | end
81 | end
82 |
83 | def test_load
84 | # reset Locale's internal cache
85 | IRB::Locale.class_variable_set(:@@loaded, [])
86 | # Because error.rb files define the same class, loading them causes method redefinition warnings.
87 | original_verbose = $VERBOSE
88 | $VERBOSE = nil
89 |
90 | jp_local = IRB::Locale.new("ja_JP.UTF-8")
91 | jp_local.load("irb/error.rb")
92 | msg = IRB::CantReturnToNormalMode.new.message
93 | assert_equal("Normalモードに戻れません.", msg)
94 |
95 | # reset Locale's internal cache
96 | IRB::Locale.class_variable_set(:@@loaded, [])
97 |
98 | en_local = IRB::Locale.new("en_US.UTF-8")
99 | en_local.load("irb/error.rb")
100 | msg = IRB::CantReturnToNormalMode.new.message
101 | assert_equal("Can't return to normal mode.", msg)
102 | ensure
103 | # before turning warnings back on, load the error.rb file again to avoid warnings in other tests
104 | IRB::Locale.new.load("irb/error.rb")
105 | $VERBOSE = original_verbose
106 | end
107 |
108 | def test_find
109 | jp_local = IRB::Locale.new("ja_JP.UTF-8")
110 | path = jp_local.find("irb/error.rb")
111 | assert_include(path, "/lib/irb/lc/ja/error.rb")
112 |
113 | en_local = IRB::Locale.new("en_US.UTF-8")
114 | path = en_local.find("irb/error.rb")
115 | assert_include(path, "/lib/irb/lc/error.rb")
116 | end
117 | end
118 | end
119 |
--------------------------------------------------------------------------------
/test/irb/test_option.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: false
2 | require_relative "helper"
3 |
4 | module TestIRB
5 | class OptionTest < TestCase
6 | def test_end_of_option
7 | bug4117 = '[ruby-core:33574]'
8 | bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
9 | libdir = File.expand_path("../../lib", __dir__)
10 | reline_libdir = Gem.loaded_specs["reline"].full_gem_path + "/lib"
11 | status = assert_in_out_err(bundle_exec + %W[-W0 -I#{libdir} -I#{reline_libdir} -rirb -e IRB.start(__FILE__) -- -f --], "", //, [], bug4117)
12 | assert(status.success?, bug4117)
13 | end
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/test/irb/test_pager.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: false
2 | require 'irb/pager'
3 |
4 | require_relative 'helper'
5 |
6 | module TestIRB
7 | class PagerTest < TestCase
8 | def test_take_first_page
9 | assert_equal ['a' * 40, true], IRB::Pager.take_first_page(10, 4) {|io| io.puts 'a' * 41; raise 'should not reach here' }
10 | assert_equal ["a\nb\na\nb\n", true], IRB::Pager.take_first_page(10, 4) {|io| 10.times { io.puts "a\nb\n" } }
11 | assert_equal ["a\n\n\na\n", true], IRB::Pager.take_first_page(10, 4) {|io| 10.times { io.puts "a\n\n\n" } }
12 | assert_equal ["11\n" * 4, true], IRB::Pager.take_first_page(10, 4) {|io| 10.times { io.write 1; io.puts 1 } }
13 | assert_equal ["\n" * 4, true], IRB::Pager.take_first_page(10, 4) {|io| 10.times { io.write nil; io.puts nil } }
14 | assert_equal ['a' * 39, false], IRB::Pager.take_first_page(10, 4) {|io| io.write 'a' * 39 }
15 | assert_equal ['a' * 39 + 'b', false], IRB::Pager.take_first_page(10, 4) {|io| io.write 'a' * 39 + 'b' }
16 | assert_equal ['a' * 39 + 'b', true], IRB::Pager.take_first_page(10, 4) {|io| io.write 'a' * 39 + 'bc' }
17 | assert_equal ["a\nb\nc\nd\n", false], IRB::Pager.take_first_page(10, 4) {|io| io.write "a\nb\nc\nd\n" }
18 | assert_equal ["a\nb\nc\nd\n", true], IRB::Pager.take_first_page(10, 4) {|io| io.write "a\nb\nc\nd\ne" }
19 | assert_equal ['a' * 15 + "\n" + 'b' * 20, true], IRB::Pager.take_first_page(10, 4) {|io| io.puts 'a' * 15; io.puts 'b' * 30 }
20 | assert_equal ["\e[31mA\e[0m" * 10 + 'x' * 30, true], IRB::Pager.take_first_page(10, 4) {|io| io.puts "\e[31mA\e[0m" * 10 + 'x' * 31 }
21 | text, overflow = IRB::Pager.take_first_page(10, 4) {|io| 41.times { io.write "\e[31mA\e[0m" } }
22 | assert_equal ['A' * 40, true], [text.gsub(/\e\[\d+m/, ''), overflow]
23 | text, overflow = IRB::Pager.take_first_page(10, 4) {|io| 41.times { io.write "\e[31mAAA\e[0m" } }
24 | assert_equal ['A' * 40, true], [text.gsub(/\e\[\d+m/, ''), overflow]
25 | end
26 | end
27 |
28 | class PageOverflowIOTest < TestCase
29 | def test_overflow
30 | actual_events = []
31 | overflow_callback = ->(lines) do
32 | actual_events << [:callback_called, lines]
33 | end
34 | out = IRB::Pager::PageOverflowIO.new(10, 4, overflow_callback)
35 | out.puts 'a' * 15
36 | out.write 'b' * 15
37 |
38 | actual_events << :before_write
39 | out.write 'c' * 1000
40 | actual_events << :after_write
41 |
42 | out.puts 'd' * 1000
43 | out.write 'e' * 1000
44 |
45 | expected_events = [
46 | :before_write,
47 | [:callback_called, ['a' * 10, 'a' * 5 + "\n", 'b' * 10, 'b' * 5 + 'c' * 5]],
48 | :after_write,
49 | ]
50 | assert_equal expected_events, actual_events
51 |
52 | expected_whole_content = 'a' * 15 + "\n" + 'b' * 15 + 'c' * 1000 + 'd' * 1000 + "\n" + 'e' * 1000
53 | assert_equal expected_whole_content, out.string
54 | end
55 |
56 | def test_callback_delay
57 | actual_events = []
58 | overflow_callback = ->(lines) do
59 | actual_events << [:callback_called, lines]
60 | end
61 | out = IRB::Pager::PageOverflowIO.new(10, 4, overflow_callback, delay: 0.2)
62 | out.write 'a' * 1000
63 | assert_equal ['a' * 10] * 4, out.first_page_lines
64 | out.write 'b'
65 | actual_events << :before_delay
66 | sleep 0.2
67 | out.write 'c'
68 | actual_events << :after_delay
69 | out.write 'd'
70 | assert_equal 'a' * 1000 + 'bcd', out.string
71 | assert_equal [:before_delay, [:callback_called, ['a' * 10] * 4], :after_delay], actual_events
72 | end
73 |
74 | def test_zero_width
75 | out = IRB::Pager::PageOverflowIO.new(0, 0, ->*{})
76 | 100.times { out.write 'a' }
77 | assert_equal [true, [], 'a' * 100], [out.multipage?, out.first_page_lines, out.string]
78 | out = IRB::Pager::PageOverflowIO.new(10, 0, ->*{})
79 | 100.times { out.write 'a' }
80 | assert_equal [true, [], 'a' * 100], [out.multipage?, out.first_page_lines, out.string]
81 | out = IRB::Pager::PageOverflowIO.new(0, 10, ->*{})
82 | 100.times { out.write 'a' }
83 | assert_equal [true, [], 'a' * 100], [out.multipage?, out.first_page_lines, out.string]
84 | end
85 | end
86 | end
87 |
--------------------------------------------------------------------------------
/test/irb/test_raise_exception.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: false
2 | require "tmpdir"
3 |
4 | require_relative "helper"
5 |
6 | module TestIRB
7 | class RaiseExceptionTest < TestCase
8 | def test_raise_exception_with_nil_backtrace
9 | bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
10 | libdir = File.expand_path("../../lib", __dir__)
11 | reline_libdir = Gem.loaded_specs["reline"].full_gem_path + "/lib"
12 | assert_in_out_err(bundle_exec + %W[-I#{libdir} -I#{reline_libdir} -rirb -W0 -e IRB.start(__FILE__) -- -f --], <<-IRB, /#/, [])
13 | raise Exception.new("foo").tap {|e| def e.backtrace; nil; end }
14 | IRB
15 | end
16 |
17 | def test_raise_exception_with_message_exception
18 | bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
19 | libdir = File.expand_path("../../lib", __dir__)
20 | reline_libdir = Gem.loaded_specs["reline"].full_gem_path + "/lib"
21 | expected = /#\nbacktraces are hidden because bar was raised when processing them/
22 | assert_in_out_err(bundle_exec + %W[-I#{libdir} -I#{reline_libdir} -rirb -W0 -e IRB.start(__FILE__) -- -f --], <<-IRB, expected, [])
23 | e = Exception.new("foo")
24 | def e.message; raise 'bar'; end
25 | raise e
26 | IRB
27 | end
28 |
29 | def test_raise_exception_with_message_inspect_exception
30 | bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
31 | libdir = File.expand_path("../../lib", __dir__)
32 | reline_libdir = Gem.loaded_specs["reline"].full_gem_path + "/lib"
33 | expected = /Uninspectable exception occurred/
34 | assert_in_out_err(bundle_exec + %W[-I#{libdir} -I#{reline_libdir} -rirb -W0 -e IRB.start(__FILE__) -- -f --], <<-IRB, expected, [])
35 | e = Exception.new("foo")
36 | def e.message; raise; end
37 | def e.inspect; raise; end
38 | raise e
39 | IRB
40 | end
41 |
42 | def test_raise_exception_with_invalid_byte_sequence
43 | pend if RUBY_ENGINE == 'truffleruby' || /mswin|mingw/ =~ RUBY_PLATFORM
44 | bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
45 | libdir = File.expand_path("../../lib", __dir__)
46 | reline_libdir = Gem.loaded_specs["reline"].full_gem_path + "/lib"
47 | assert_in_out_err(bundle_exec + %W[-I#{libdir} -I#{reline_libdir} -rirb -W0 -e IRB.start(__FILE__) -- -f --], <<~IRB, /A\\xF3B \(StandardError\)/, [])
48 | raise StandardError, "A\\xf3B"
49 | IRB
50 | end
51 |
52 | def test_raise_exception_with_different_encoding_containing_invalid_byte_sequence
53 | backup_home = ENV["HOME"]
54 | Dir.mktmpdir("test_irb_raise_no_backtrace_exception_#{$$}") do |tmpdir|
55 | ENV["HOME"] = tmpdir
56 |
57 | bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
58 | libdir = File.expand_path("../../lib", __dir__)
59 | reline_libdir = Gem.loaded_specs["reline"].full_gem_path + "/lib"
60 | File.open("#{tmpdir}/euc.rb", 'w') do |f|
61 | f.write(<<~EOF)
62 | # encoding: euc-jp
63 |
64 | def raise_euc_with_invalid_byte_sequence
65 | raise "\xA4\xA2\\xFF"
66 | end
67 | EOF
68 | end
69 | env = {}
70 | %w(LC_MESSAGES LC_ALL LC_CTYPE LANG).each {|n| env[n] = "ja_JP.UTF-8" }
71 | # TruffleRuby warns when the locale does not exist
72 | env['TRUFFLERUBYOPT'] = "#{ENV['TRUFFLERUBYOPT']} --log.level=SEVERE" if RUBY_ENGINE == 'truffleruby'
73 | args = [env] + bundle_exec + %W[-I#{libdir} -I#{reline_libdir} -rirb -C #{tmpdir} -W0 -e IRB.start(__FILE__) -- -f --]
74 | error = /raise_euc_with_invalid_byte_sequence': あ\\xFF \(RuntimeError\)/
75 | assert_in_out_err(args, <<~IRB, error, [], encoding: "UTF-8")
76 | require_relative 'euc'
77 | raise_euc_with_invalid_byte_sequence
78 | IRB
79 | end
80 | ensure
81 | ENV["HOME"] = backup_home
82 | end
83 | end
84 | end
85 |
--------------------------------------------------------------------------------
/test/irb/test_tracer.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: false
2 | require 'tempfile'
3 | require 'irb'
4 |
5 | require_relative "helper"
6 |
7 | module TestIRB
8 | class ContextWithTracerIntegrationTest < IntegrationTestCase
9 | def setup
10 | super
11 |
12 | omit "Tracer gem is not available when running on TruffleRuby" if RUBY_ENGINE == "truffleruby"
13 |
14 | @envs.merge!("NO_COLOR" => "true")
15 | end
16 |
17 | def example_ruby_file
18 | <<~'RUBY'
19 | class Foo
20 | def self.foo
21 | 100
22 | end
23 | end
24 |
25 | def bar(obj)
26 | obj.foo
27 | end
28 |
29 | binding.irb
30 | RUBY
31 | end
32 |
33 | def test_use_tracer_enabled_when_gem_is_unavailable
34 | write_rc <<~RUBY
35 | # Simulate the absence of the tracer gem
36 | ::Kernel.send(:alias_method, :irb_original_require, :require)
37 |
38 | ::Kernel.define_method(:require) do |name|
39 | raise LoadError, "cannot load such file -- tracer (test)" if name.match?("tracer")
40 | ::Kernel.send(:irb_original_require, name)
41 | end
42 |
43 | IRB.conf[:USE_TRACER] = true
44 | RUBY
45 |
46 | write_ruby example_ruby_file
47 |
48 | output = run_ruby_file do
49 | type "bar(Foo)"
50 | type "exit"
51 | end
52 |
53 | assert_include(output, "Tracer extension of IRB is enabled but tracer gem wasn't found.")
54 | end
55 |
56 | def test_use_tracer_enabled_when_gem_is_available
57 | write_rc <<~RUBY
58 | IRB.conf[:USE_TRACER] = true
59 | RUBY
60 |
61 | write_ruby example_ruby_file
62 |
63 | output = run_ruby_file do
64 | type "bar(Foo)"
65 | type "exit"
66 | end
67 |
68 | assert_include(output, "Object#bar at")
69 | assert_include(output, "Foo.foo at")
70 | assert_include(output, "Foo.foo #=> 100")
71 | assert_include(output, "Object#bar #=> 100")
72 |
73 | # Test that the tracer output does not include IRB's own files
74 | assert_not_include(output, "irb/workspace.rb")
75 | end
76 |
77 | def test_use_tracer_is_disabled_by_default
78 | write_ruby example_ruby_file
79 |
80 | output = run_ruby_file do
81 | type "bar(Foo)"
82 | type "exit"
83 | end
84 |
85 | assert_not_include(output, "#depth:")
86 | assert_not_include(output, "Foo.foo")
87 | end
88 |
89 | end
90 | end
91 |
--------------------------------------------------------------------------------
/test/irb/test_type_completor.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | # Run test only when Ruby >= 3.0 and repl_type_completor is available
4 | return unless RUBY_VERSION >= '3.0.0'
5 | return if RUBY_ENGINE == 'truffleruby' # needs endless method definition
6 | begin
7 | require 'repl_type_completor'
8 | rescue LoadError
9 | return
10 | end
11 |
12 | require 'irb'
13 | require 'tempfile'
14 | require_relative './helper'
15 |
16 | module TestIRB
17 | class TypeCompletorTest < TestCase
18 | DummyContext = Struct.new(:irb_path)
19 |
20 | def setup
21 | ReplTypeCompletor.load_rbs unless ReplTypeCompletor.rbs_loaded?
22 | context = DummyContext.new('(irb)')
23 | @completor = IRB::TypeCompletor.new(context)
24 | end
25 |
26 | def empty_binding
27 | binding
28 | end
29 |
30 | def test_build_completor
31 | IRB.init_config(nil)
32 | verbose, $VERBOSE = $VERBOSE, nil
33 | original_completor = IRB.conf[:COMPLETOR]
34 | workspace = IRB::WorkSpace.new(Object.new)
35 | @context = IRB::Context.new(nil, workspace, TestInputMethod.new)
36 | IRB.conf[:COMPLETOR] = nil
37 | expected_default_completor = RUBY_VERSION >= '3.4' ? 'IRB::TypeCompletor' : 'IRB::RegexpCompletor'
38 | assert_equal expected_default_completor, @context.send(:build_completor).class.name
39 | IRB.conf[:COMPLETOR] = :type
40 | assert_equal 'IRB::TypeCompletor', @context.send(:build_completor).class.name
41 | ensure
42 | $VERBOSE = verbose
43 | IRB.conf[:COMPLETOR] = original_completor
44 | end
45 |
46 | def assert_completion(preposing, target, binding: empty_binding, include: nil, exclude: nil)
47 | raise ArgumentError if include.nil? && exclude.nil?
48 | candidates = @completor.completion_candidates(preposing, target, '', bind: binding)
49 | assert ([*include] - candidates).empty?, "Expected #{candidates} to include #{include}" if include
50 | assert (candidates & [*exclude]).empty?, "Expected #{candidates} not to include #{exclude}" if exclude
51 | end
52 |
53 | def assert_doc_namespace(preposing, target, namespace, binding: empty_binding)
54 | @completor.completion_candidates(preposing, target, '', bind: binding)
55 | assert_equal namespace, @completor.doc_namespace(preposing, target, '', bind: binding)
56 | end
57 |
58 | def test_type_completion
59 | bind = eval('num = 1; binding')
60 | assert_completion('num.times.map(&:', 'ab', binding: bind, include: 'abs')
61 | assert_doc_namespace('num.chr.', 'upcase', 'String#upcase', binding: bind)
62 | end
63 |
64 | def test_inspect
65 | assert_match(/\AReplTypeCompletor.*\z/, @completor.inspect)
66 | end
67 |
68 | def test_empty_completion
69 | candidates = @completor.completion_candidates('(', ')', '', bind: binding)
70 | assert_equal [], candidates
71 | assert_doc_namespace('(', ')', nil)
72 | end
73 |
74 | def test_command_completion
75 | binding.eval("some_var = 1")
76 | # completion for help command's argument should only include command names
77 | assert_include(@completor.completion_candidates('help ', 's', '', bind: binding), 'show_source')
78 | assert_not_include(@completor.completion_candidates('help ', 's', '', bind: binding), 'some_var')
79 |
80 | assert_include(@completor.completion_candidates('', 'show_s', '', bind: binding), 'show_source')
81 | assert_not_include(@completor.completion_candidates(';', 'show_s', '', bind: binding), 'show_source')
82 | end
83 | end
84 |
85 | class TypeCompletorIntegrationTest < IntegrationTestCase
86 | def test_type_completor
87 | write_rc <<~RUBY
88 | IRB.conf[:COMPLETOR] = :type
89 | RUBY
90 |
91 | write_ruby <<~'RUBY'
92 | binding.irb
93 | RUBY
94 |
95 | output = run_ruby_file do
96 | type "irb_info"
97 | type "sleep 0.01 until ReplTypeCompletor.rbs_loaded?"
98 | type "completor = IRB.CurrentContext.io.instance_variable_get(:@completor);"
99 | type "n = 10"
100 | type "puts completor.completion_candidates 'a = n.abs;', 'a.b', '', bind: binding"
101 | type "puts completor.doc_namespace 'a = n.chr;', 'a.encoding', '', bind: binding"
102 | type "exit!"
103 | end
104 | assert_match(/Completion: Autocomplete, ReplTypeCompletor/, output)
105 | assert_match(/a\.bit_length/, output)
106 | assert_match(/String#encoding/, output)
107 | end
108 | end
109 | end
110 |
--------------------------------------------------------------------------------
/test/irb/test_workspace.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: false
2 | require 'tempfile'
3 | require 'irb'
4 | require 'irb/workspace'
5 | require 'irb/color'
6 |
7 | require_relative "helper"
8 |
9 | module TestIRB
10 | class WorkSpaceTest < TestCase
11 | def test_code_around_binding
12 | IRB.conf[:USE_COLORIZE] = false
13 | Tempfile.create('irb') do |f|
14 | code = <<~RUBY
15 | # 1
16 | # 2
17 | IRB::WorkSpace.new(binding) # 3
18 | # 4
19 | # 5
20 | RUBY
21 | f.print(code)
22 | f.close
23 |
24 | workspace = eval(code, binding, f.path)
25 | assert_equal(<<~EOS, without_term { workspace.code_around_binding })
26 |
27 | From: #{f.path} @ line 3 :
28 |
29 | 1: # 1
30 | 2: # 2
31 | => 3: IRB::WorkSpace.new(binding) # 3
32 | 4: # 4
33 | 5: # 5
34 |
35 | EOS
36 | end
37 | ensure
38 | IRB.conf.delete(:USE_COLORIZE)
39 | end
40 |
41 | def test_code_around_binding_with_existing_unreadable_file
42 | pend 'chmod cannot make file unreadable on windows' if windows?
43 | pend 'skipped in root privilege' if Process.uid == 0
44 |
45 | Tempfile.create('irb') do |f|
46 | code = "IRB::WorkSpace.new(binding)\n"
47 | f.print(code)
48 | f.close
49 |
50 | File.chmod(0, f.path)
51 |
52 | workspace = eval(code, binding, f.path)
53 | assert_equal(nil, workspace.code_around_binding)
54 | end
55 | end
56 |
57 | def test_code_around_binding_with_script_lines__
58 | IRB.conf[:USE_COLORIZE] = false
59 | with_script_lines do |script_lines|
60 | Tempfile.create('irb') do |f|
61 | code = "IRB::WorkSpace.new(binding)\n"
62 | script_lines[f.path] = code.split(/^/)
63 |
64 | workspace = eval(code, binding, f.path)
65 | assert_equal(<<~EOS, without_term { workspace.code_around_binding })
66 |
67 | From: #{f.path} @ line 1 :
68 |
69 | => 1: IRB::WorkSpace.new(binding)
70 |
71 | EOS
72 | end
73 | end
74 | ensure
75 | IRB.conf.delete(:USE_COLORIZE)
76 | end
77 |
78 | def test_code_around_binding_on_irb
79 | workspace = eval("IRB::WorkSpace.new(binding)", binding, "(irb)")
80 | assert_equal(nil, workspace.code_around_binding)
81 | end
82 |
83 | def test_toplevel_binding_local_variables
84 | bug17623 = '[ruby-core:102468]'
85 | bundle_exec = ENV.key?('BUNDLE_GEMFILE') ? ['-rbundler/setup'] : []
86 | top_srcdir = "#{__dir__}/../.."
87 | reline_libdir = Gem.loaded_specs["reline"].full_gem_path + "/lib"
88 | irb_path = nil
89 | %w[exe libexec].find do |dir|
90 | irb_path = "#{top_srcdir}/#{dir}/irb"
91 | File.exist?(irb_path)
92 | end or omit 'irb command not found'
93 | assert_in_out_err(bundle_exec + ['-W0', "-I#{top_srcdir}/lib", "-I#{reline_libdir}", "-C#{top_srcdir}", '-e', <<~RUBY, '--', '-f', '--'], 'binding.local_variables', /\[:_\]/, [], bug17623)
94 | version = 'xyz' # typical rubygems loading file
95 | load('#{irb_path}')
96 | RUBY
97 | end
98 |
99 | private
100 |
101 | def with_script_lines
102 | script_lines = nil
103 | debug_lines = {}
104 | Object.class_eval do
105 | if defined?(SCRIPT_LINES__)
106 | script_lines = SCRIPT_LINES__
107 | remove_const :SCRIPT_LINES__
108 | end
109 | const_set(:SCRIPT_LINES__, debug_lines)
110 | end
111 | yield debug_lines
112 | ensure
113 | Object.class_eval do
114 | remove_const :SCRIPT_LINES__
115 | const_set(:SCRIPT_LINES__, script_lines) if script_lines
116 | end
117 | end
118 |
119 | def without_term
120 | env = ENV.to_h.dup
121 | ENV.delete('TERM')
122 | yield
123 | ensure
124 | ENV.replace(env)
125 | end
126 | end
127 | end
128 |
--------------------------------------------------------------------------------
/test/lib/helper.rb:
--------------------------------------------------------------------------------
1 | require "test/unit"
2 | require "core_assertions"
3 |
4 | Test::Unit::TestCase.include Test::Unit::CoreAssertions
5 |
6 | module Test
7 | module Unit
8 | class TestCase
9 | def windows? platform = RUBY_PLATFORM
10 | /mswin|mingw/ =~ platform
11 | end
12 | end
13 | end
14 | end
15 |
--------------------------------------------------------------------------------