rich-ri 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,110 @@
1
+ # Development checks
2
+
3
+ Use [Contributing](../CONTRIBUTING.md) for the usual edit, test and PR workflow.
4
+ The commands here cover CI, dependency compatibility and performance work.
5
+
6
+ ## Documentation checks
7
+
8
+ For documentation changes, run:
9
+
10
+ ```sh
11
+ bundle exec rake docs:check
12
+ ```
13
+
14
+ This checks local links, spelling, the generated manual and runnable examples
15
+ from the usage and configuration guides. It does not need the optional shells
16
+ or system manual tools.
17
+
18
+ CI uses these checks for changes limited to Markdown guides in `docs`, the
19
+ configuration example, PNG comparison images, the PR template and the root README,
20
+ architecture, contribution, changelog, security and code of conduct documents.
21
+ Pull requests also run the commit checks.
22
+
23
+ Code, dependencies, scripts, workflows, test fixtures, the generated manual and
24
+ unrecognized paths run the full suite. CI considers the entire PR, including
25
+ deleted and renamed files; an unavailable comparison also runs the full suite.
26
+ The required `ci` check verifies that every expected job succeeded before a
27
+ change can merge.
28
+
29
+ Releases always run the full suite, including workflow dry runs. Scheduled runs
30
+ check the dependency advisory database.
31
+
32
+ ## Shell tests
33
+
34
+ Run completion tests in all three shells with:
35
+
36
+ ```sh
37
+ bundle exec rake test:shells
38
+ ```
39
+
40
+ The task uses local Bash, Zsh and Fish when all are available and Bash can load
41
+ bash-completion 2.x. Otherwise, it uses a running Docker engine or, if unavailable,
42
+ Podman. `bundle exec rake check` includes this task and requires every shell test
43
+ to pass. The shorter `bundle exec rake` run skips unavailable local integrations.
44
+
45
+ To run in an isolated environment even when the shells are installed:
46
+
47
+ ```sh
48
+ bundle exec rake test:shells:container
49
+ ```
50
+
51
+ The first build downloads the image and installs the locked bundle; later builds
52
+ reuse those layers. Only the project files needed for testing are copied into the
53
+ image. Tests run as an unprivileged user with temporary storage and no network.
54
+ The container uses its pinned Linux/Ruby environment and `Gemfile.lock`, regardless
55
+ of the host Ruby version. CI checks this route as well as native shell integrations
56
+ on Linux and macOS.
57
+
58
+ Set `RICH_RI_CONTAINER_RUNTIME=docker` or `RICH_RI_CONTAINER_RUNTIME=podman` to
59
+ select an engine when running in a container. For example:
60
+
61
+ ```sh
62
+ RICH_RI_CONTAINER_RUNTIME=podman bundle exec rake test:shells:container
63
+ ```
64
+
65
+ ## Compatibility checks
66
+
67
+ The main `Gemfile.lock` fixes development dependencies. CI also resolves current
68
+ versions with `bundle update` in a fresh checkout. Run that check in a separate
69
+ checkout to keep your working lockfile intact.
70
+
71
+ To test the minimum runtime dependencies, use Ruby 3.4 with Bash, Zsh, Fish and
72
+ bash-completion 2.x installed locally, then run:
73
+
74
+ ```sh
75
+ BUNDLE_GEMFILE=gemfiles/legacy.gemfile bundle install
76
+ BUNDLE_GEMFILE=gemfiles/legacy.gemfile bundle exec ruby -rrdoc/rdoc -e \
77
+ 'RDoc::RDoc.new.document(ARGV)' -- --ri --quiet --op tmp/legacy-ri test/fixtures/example.rb
78
+ BUNDLE_GEMFILE=gemfiles/minimum.gemfile bundle install
79
+ LEGACY_RI_STORE="$PWD/tmp/legacy-ri" BUNDLE_GEMFILE=gemfiles/minimum.gemfile \
80
+ RICH_RI_REQUIRE_SHELLS=1 bundle exec ruby bin/test-compatibility
81
+ ```
82
+
83
+ The first bundle generates a store using RDoc 6.14. The second runs the runtime
84
+ tests using the minimum direct dependencies compatible with RDoc 8.1, including
85
+ lookup and completion against that older store. Maintenance-only tests and the
86
+ optional server/profiler modes use the main bundle; missing-gem behavior is tested
87
+ with both bundles. `LEGACY_RI_STORE` selects the test store; `RICH_RI_REQUIRE_SHELLS=1`
88
+ requires all shell integrations to be available. This check uses the selected
89
+ minimum bundle and local shells; the container task uses the main locked bundle.
90
+
91
+ When raising a runtime dependency floor, update the minimum Gemfile and
92
+ [compatibility policy](compatibility.md) in the same change.
93
+
94
+ ## Performance
95
+
96
+ ```sh
97
+ bundle exec ruby bin/benchmark --output /tmp/rich-ri-before.json
98
+ # Make the change, then measure again on the same machine and Ruby.
99
+ bundle exec ruby bin/benchmark --compare /tmp/rich-ri-before.json
100
+ ```
101
+
102
+ The benchmark measures full CLI startup, rendering and completion against a
103
+ temporary RI store built from a repository fixture. It discards one warm-up
104
+ invocation and reports the median of five runs. `--iterations=N` selects 1 to 50
105
+ runs; `--output=FILE` saves the JSON report.
106
+
107
+ Comparison exits unsuccessfully when a case slows by both more than 30% and
108
+ 50 ms. Compare under similar load with the same Ruby and dependency versions;
109
+ small timing differences are not meaningful. CI uploads a `benchmark` artifact
110
+ for each quality run so a reviewer can inspect measurements across changes.
@@ -0,0 +1,29 @@
1
+ # README comparison
2
+
3
+ `ri-vs-rich-ri.png` compares the original `ri -f ansi` formatter with rich-ri's
4
+ default terminal theme. Both panels show the complete installed `Object#then`
5
+ documentation, from the first line to the last. Its Ruby examples include method
6
+ chains, blocks, strings, constants and symbols.
7
+
8
+ The renderer captures both commands with the project's Bundler environment and
9
+ the same 58-column width, then displays their actual ANSI output using the same
10
+ dark terminal palette and monospace font. It preserves bold, italic,
11
+ underline and reverse-video attributes, including the original formatter's
12
+ inline-code styling. Long code and output lines soft-wrap at the panel width.
13
+ Every output line is included. The displayed commands show normal interactive
14
+ usage; capture adds `--no-pager --width=58` to both, and
15
+ `--no-config --color=always` to rich-ri.
16
+
17
+ To refresh the image, install the bundle, Ruby core RI documentation,
18
+ ImageMagick with SVG support and a monospace font with bold and italic faces.
19
+ The script uses Ruby's standard libraries to capture the output and generate
20
+ an SVG, then ImageMagick to convert it to PNG.
21
+
22
+ ```sh
23
+ ruby docs/images/render_comparison.rb
24
+ ```
25
+
26
+ The script clears reader configuration overrides and saves the full raw captures
27
+ and SVG in a temporary directory printed at the end. Its footer records the Ruby
28
+ and RDoc versions. Review the PNG before committing it; documentation text can
29
+ change between Ruby versions.
@@ -0,0 +1,105 @@
1
+ # Maintenance
2
+
3
+ ## Release infrastructure
4
+
5
+ `bundle exec rake github:verify` checks branch and tag protections, required CI
6
+ checks, the release environment and security settings. `rake github:setup`
7
+ applies the project policy. Both require repository administrator access.
8
+
9
+ Publication uses [RubyGems Trusted Publishing](https://guides.rubygems.org/trusted-publishing/).
10
+ The publisher must match repository `hvpaiva/rich-ri`, workflow `release.yml`
11
+ and environment `release`. Update that binding in RubyGems if the integration
12
+ changes. `github:verify` checks GitHub settings only.
13
+
14
+ ## Release
15
+
16
+ From a clean, up-to-date `main`, with `gh` authenticated and Git signing configured:
17
+
18
+ ```sh
19
+ bin/release X.Y.Z --push
20
+ ```
21
+
22
+ The command prepares the version, changelog and manual, runs the checks, opens a
23
+ release PR, waits for CI, merges, and pushes a signed tag for the merge commit.
24
+ It then watches the Release workflow. Omit `--push` to stop at the PR;
25
+ `--dry-run` previews preparation without writing files or publishing.
26
+
27
+ The workflow reruns CI, validates the tag and branch ancestry, builds one gem,
28
+ and tests installation of that file. It records a SHA-256 checksum and a GitHub
29
+ build attestation. The publication and GitHub Release jobs download that artifact
30
+ and verify its checksum. RubyGems authentication uses short-lived OIDC credentials.
31
+ `rake release` publishes the verified artifact and refuses local execution.
32
+
33
+ To rehearse the workflow without publishing a gem or creating a GitHub Release:
34
+
35
+ ```sh
36
+ gh workflow run release.yml --ref main -f dry_run=true
37
+ ```
38
+
39
+ Inspect the run in Actions. The rehearsal exercises CI, packaging, installation,
40
+ artifact transfer and attestation; it does not request RubyGems credentials.
41
+ It cannot validate the publisher's authentication with RubyGems.
42
+
43
+ ## Verify a download
44
+
45
+ Download the gem and `SHA256SUMS` from its GitHub Release, then run:
46
+
47
+ ```sh
48
+ sha256sum --check SHA256SUMS
49
+ gh attestation verify rich-ri-X.Y.Z.gem --repo hvpaiva/rich-ri --source-ref refs/tags/vX.Y.Z
50
+ ```
51
+
52
+ On macOS, use `shasum -a 256 --check SHA256SUMS`. The attestation binds the artifact
53
+ to its source and workflow. Release tags and published GitHub releases are immutable.
54
+
55
+ ## Recover an interrupted release
56
+
57
+ Run the same `bin/release` command again. It inspects existing branches, PRs,
58
+ commits and tags before continuing. It never replaces an existing tag.
59
+
60
+ If a workflow failed before publication, correct the problem and follow the
61
+ printed retry command. If RubyGems publication succeeded and only GitHub Release
62
+ creation failed, retry that job alone. When the publication result is uncertain,
63
+ check RubyGems before retrying: an accepted version must not be pushed again.
64
+
65
+ ## Urgent fixes
66
+
67
+ Only the latest released version receives fixes. If `main` contains unfinished
68
+ work for the next release, start a hotfix branch at the latest released tag.
69
+ For example, to fix `0.2.0` while `main` is preparing `0.3.0`:
70
+
71
+ ```sh
72
+ git switch -c hotfix/0.2 v0.2.0
73
+ git push -u origin hotfix/0.2
74
+ ```
75
+
76
+ Create a topic branch from it and submit the fix as a PR against `hotfix/0.2`.
77
+ Hotfix branches have the same protections as `main`. After the fix is merged,
78
+ update the local hotfix branch and run:
79
+
80
+ ```sh
81
+ bin/release 0.2.1 --branch hotfix/0.2 --push
82
+ ```
83
+
84
+ The release must match the branch's major and minor version. Bring the fix and
85
+ release notes back to `main` through a separate PR, preserving changes still under
86
+ `Unreleased` and keeping dated changelog entries newest first.
87
+
88
+ If a published version must be withdrawn, publish a fixed version first when
89
+ possible. Explain the withdrawal in its changelog entry, then use
90
+ `gem yank rich-ri -v X.Y.Z` with your personal RubyGems credentials and MFA.
91
+ Trusted publishing only grants push access. A withdrawn version cannot be reused;
92
+ publish a new version number for the replacement.
93
+
94
+ ## Dependencies and access
95
+
96
+ Dependabot proposes gem and test-image updates weekly and action updates monthly. Review the
97
+ upstream changes and run the same checks as other PRs. A runtime dependency change
98
+ may need a changelog entry even when Dependabot supplied `skip-changelog`.
99
+ Update `gemfiles/minimum.gemfile` when raising a supported dependency floor.
100
+ The scheduled audit checks the lockfile for known vulnerabilities.
101
+
102
+ Review repository administrators, RubyGems owners and publisher configuration
103
+ when maintainers change. Keep account recovery methods current and transfer both
104
+ services when handing over the project. Review changes to release code and
105
+ workflows with particular care; they run with publication or attestation permissions.
@@ -0,0 +1,51 @@
1
+ # Shell completion
2
+
3
+ Completion reads the active Ruby's RI stores, including sources selected in your
4
+ configuration file, `RI` or `--doc-dir`. It makes no network requests. It suggests
5
+ installed classes, methods, pages, options, theme names and style roles, with
6
+ descriptions where available. Invalid configuration or unavailable stores prevent
7
+ documentation-name suggestions; options and theme values remain available. Run
8
+ `rich-ri --show-config` to diagnose configuration errors.
9
+
10
+ ## Bash
11
+
12
+ Load bash-completion 2.x in your shell, then install the lazy-loaded script:
13
+
14
+ ```sh
15
+ mkdir -p "${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions"
16
+ rich-ri --completion=bash > "${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions/rich-ri"
17
+ ```
18
+
19
+ To load it in the current shell, run `source <(rich-ri --completion=bash)`.
20
+
21
+ ## Zsh
22
+
23
+ After `autoload -Uz compinit && compinit` in your `.zshrc`, add:
24
+
25
+ ```zsh
26
+ source <(rich-ri --completion=zsh)
27
+ ```
28
+
29
+ ## Fish
30
+
31
+ ```fish
32
+ mkdir -p "$__fish_config_dir/completions"
33
+ rich-ri --completion=fish > "$__fish_config_dir/completions/rich-ri.fish"
34
+ ```
35
+
36
+ ## Use the short name ri
37
+
38
+ For Bash, add these lines after loading bash-completion:
39
+
40
+ ```bash
41
+ alias ri='rich-ri'
42
+ source <(rich-ri --completion=bash)
43
+ complete -o filenames -F _rich_ri ri
44
+ ```
45
+
46
+ For Zsh, add `alias ri='rich-ri'` after the completion setup. Zsh expands aliases
47
+ for completion by default. In Fish, use `alias ri rich-ri` in your configuration;
48
+ Fish aliases inherit completions from the wrapped command.
49
+
50
+ Bash and Fish install a copy of the script; repeat the installation after upgrading
51
+ rich-ri. The Zsh setup loads the current script when a new shell starts.
@@ -0,0 +1,95 @@
1
+ # Troubleshooting
2
+
3
+ ## A page is missing
4
+
5
+ Check `rich-ri --list` for known classes and `rich-ri --list-doc-dirs` for the
6
+ directories being searched. Try `ri NAME` with the original reader as well.
7
+ If you use an alias, `command ri NAME` bypasses it in Bash and Zsh.
8
+
9
+ For a gem installed without documentation, run `gem rdoc GEM_NAME --ri`.
10
+ For Ruby's core classes, install the documentation package supplied by your Ruby
11
+ manager or operating system. Switch to the intended Ruby before generating or
12
+ reading documentation: each installation has its own stores.
13
+
14
+ See [documentation sources](usage.md#documentation-sources-and-missing-pages)
15
+ to read documentation from a project directory.
16
+
17
+ ## An RI cache has an incompatible format
18
+
19
+ RDoc uses different serialized markup representations on Ruby 3 and Ruby 4.
20
+ Keep documentation with the Ruby installation that generated it. Regenerate gem
21
+ documentation with `gem rdoc GEM_NAME --ri` under the Ruby you are using now.
22
+
23
+ For core documentation, obtain the source for that Ruby version, then generate a
24
+ new RI store with the current RDoc. For example, from the Ruby source directory:
25
+
26
+ ```sh
27
+ rdoc --ri --op doc/ri
28
+ rich-ri --no-standard-docs --doc-dir doc/ri Array
29
+ ```
30
+
31
+ Use [doc_dirs](configuration.md#file-keys) to keep using the new store. Do not
32
+ copy a cache from another Ruby installation as a substitute for regeneration.
33
+
34
+ ## Colors are missing or hard to read
35
+
36
+ Colors are enabled automatically on a terminal. `NO_COLOR`, `TERM=dumb` and
37
+ redirected output disable automatic colors. Try `rich-ri --color=always NAME`
38
+ to force them, or `rich-ri --no-color NAME` for plain output.
39
+
40
+ The default theme uses the terminal's palette. Adjust a role such as
41
+ `--style='comment=bright_black'`, or select `--theme=light` or `--theme=dark`.
42
+ [Themes and styles](configuration.md#themes-and-styles) describes persistent settings.
43
+
44
+ Ruby highlighting is built in. Shell examples and other tagged languages need
45
+ bat on `PATH`; confirm it with `bat --version`. If bat fails or cannot safely
46
+ highlight an example, that example stays plain. A failure disables bat for the
47
+ rest of the page. `--shell-theme` and `--bat-theme` must name an installed bat
48
+ theme; list them with `bat --list-themes`.
49
+
50
+ ## The pager behaves differently
51
+
52
+ Try `rich-ri --no-pager NAME` to isolate the reader from the pager.
53
+ `RI_PAGER` takes precedence over `PAGER`; a configured pager command and
54
+ `--pager-command` can also select it. Check the effective settings with
55
+ `rich-ri --show-config`. For less, rich-ri adds `-R` so ANSI colors are displayed.
56
+
57
+ ## An optional RDoc mode cannot start
58
+
59
+ `--server` needs the `webrick` gem; `--profile` needs the `profile` gem. Install
60
+ the named gem with the active Ruby, then repeat the command. For example,
61
+ `gem install webrick` enables the web server. With `bundle exec`, include the
62
+ gem in the current Gemfile as well.
63
+
64
+ These gems are not required for terminal lookup or completion. See
65
+ [optional RDoc modes](usage.md#optional-rdoc-modes) for their behavior.
66
+
67
+ ## Configuration prevents startup
68
+
69
+ `rich-ri --config-path` prints the selected file. `--no-config` skips that file;
70
+ environment variables still apply. Help and version output remain available
71
+ even when the file contains invalid YAML or settings.
72
+
73
+ See [configuration troubleshooting](configuration.md#troubleshooting) for
74
+ precedence, YAML types and path handling.
75
+
76
+ ## Tab completion is missing
77
+
78
+ Follow the setup for your shell in [shell completion](shell-completion.md).
79
+ After changing a shell startup file, open a new shell or load the file again.
80
+ For an alias, include the alias setup from that guide.
81
+
82
+ If options complete but documentation names do not, check `--show-config` and
83
+ `--list-doc-dirs`. Invalid settings or an unreadable RI store prevent name
84
+ discovery. Bash and Fish completion scripts should be reinstalled after an upgrade.
85
+
86
+ ## Report a problem
87
+
88
+ Include the command, expected result, actual result and a small document that
89
+ reproduces it. Provide `rich-ri --version`, `ruby --version`, your operating
90
+ system, shell and terminal, and the output of `gem list --local rdoc prism reline`.
91
+ For shell highlighting, include `bat --version` too.
92
+
93
+ Review any configuration output before sharing it; pager arguments and file
94
+ paths can contain private information. Open a [bug report](https://github.com/hvpaiva/rich-ri/issues/new?template=bug.yml).
95
+ Use the [private security channel](../SECURITY.md) for vulnerabilities.
data/docs/usage.md ADDED
@@ -0,0 +1,147 @@
1
+ # Reading documentation
2
+
3
+ Names follow RI conventions. Use `Class#method` for instance methods,
4
+ `Class::method` for class methods, and `Class.method` to search both.
5
+ Quote names containing shell punctuation, such as `rich-ri 'Array.[]'`.
6
+ Write long options in full; abbreviations are not accepted.
7
+
8
+ Run `rich-ri` without arguments for RI's interactive lookup. Its Tab completion
9
+ is extended with documentation pages and method prefixes such as `String#`,
10
+ `String.` and `String::`; submit an empty line to leave. With shell completion
11
+ installed, you can also find classes, methods, pages and options before running a command:
12
+
13
+ ```text
14
+ rich-ri Str<Tab>
15
+ rich-ri String#<Tab>
16
+ rich-ri String.<Tab>
17
+ rich-ri ruby:<Tab>
18
+ rich-ri --<Tab>
19
+ ```
20
+
21
+ In less, use `/` to search, `n` for the next match, Space for the next page and
22
+ `q` to return. `--no-pager` writes directly to stdout.
23
+
24
+ Headings keep their RDoc level markers (`=`, `==`, through `======`), in the
25
+ same style as the heading text. Horizontal separators use plain hyphens in a
26
+ muted style. Search for `^=== ` to find level-three headings or `^---` to find
27
+ separators, then use `n` and `N` to move between matches. These markers remain
28
+ available with colors disabled.
29
+
30
+ ```sh
31
+ rich-ri --all Array
32
+ rich-ri --width=72 String#scan
33
+ rich-ri --color=always Array#map | less -R
34
+ rich-ri --no-color Hash
35
+ rich-ri --format=markdown Array#map
36
+ ```
37
+
38
+ `--format` selects an original RDoc formatter. Otherwise, disabling colors
39
+ preserves rich-ri's page layout. Code blocks keep their content and indentation;
40
+ prose wraps by visible terminal width, including wide Unicode characters.
41
+
42
+ ## Documentation sources and missing pages
43
+
44
+ rich-ri reads installed documentation; it does not download documentation or
45
+ generate it while you browse. `rich-ri --list-doc-dirs` shows the searched paths,
46
+ and `rich-ri --list` lists known classes.
47
+
48
+ If a gem was installed with `--no-document`, generate its RI documentation with
49
+ `gem rdoc GEM_NAME --ri`. Ruby core documentation depends on how your Ruby was
50
+ installed; install the documentation package supplied by your Ruby manager or OS.
51
+
52
+ ### Read your project's documentation
53
+
54
+ Save this small example as `greeter.rb`:
55
+
56
+ ```ruby
57
+ class Greeter
58
+ # Returns a greeting for the given name.
59
+ #
60
+ # Greeter.new.greet("Ruby") # => "Hello, Ruby!"
61
+ def greet(name)
62
+ "Hello, #{name}!"
63
+ end
64
+ end
65
+ ```
66
+
67
+ Generate its documentation and read the method:
68
+
69
+ ```sh
70
+ rdoc --ri --quiet --op doc/ri greeter.rb
71
+ rich-ri --no-standard-docs --doc-dir doc/ri --no-color --no-pager --width=60 Greeter#greet
72
+ ```
73
+
74
+ The output below abbreviates the current directory as `.`:
75
+
76
+ ```text
77
+ = Greeter#greet
78
+
79
+ (from ./doc/ri)
80
+ ------------------------------------------------------------
81
+ greet(name)
82
+
83
+ ------------------------------------------------------------
84
+
85
+ Returns a greeting for the given name.
86
+
87
+ Greeter.new.greet("Ruby") # => "Hello, Ruby!"
88
+ ```
89
+
90
+ To use the same documentation sources for lookup and shell completion, configure
91
+ `RI` in the current shell:
92
+
93
+ | Shell | Command |
94
+ | --- | --- |
95
+ | Bash or Zsh | `export RI='--no-standard-docs --doc-dir doc/ri'` |
96
+ | Fish | `set -gx RI '--no-standard-docs --doc-dir doc/ri'` |
97
+
98
+ Only open RI stores you trust. RI caches use Ruby Marshal serialization; see
99
+ [security policy](../SECURITY.md) for the trust boundary.
100
+
101
+ ## Manual
102
+
103
+ `rich-ri --help` lists the options. `rich-ri --man` opens the full manual.
104
+ To make the page available through `man rich-ri`, run `rich-ri --install-man`.
105
+ The command prints its destination and any needed `MANPATH` setting. Repeat it
106
+ when upgrading. Use `--install-man=DIR` to choose another `man1` directory.
107
+
108
+ ## Optional programs
109
+
110
+ Ruby highlighting is built in. bat adds highlighting for shell commands and
111
+ other tagged languages. less, or another RI-compatible pager, provides scrolling
112
+ and search. `man` opens the bundled manual. These programs are available from
113
+ your operating system's package manager.
114
+
115
+ Without bat, those code blocks stay plain. If no pager is available, documentation
116
+ is written to the terminal; `--no-pager` selects this behavior explicitly.
117
+ `--help` and `--install-man` work without the `man` program.
118
+
119
+ ## Optional RDoc modes
120
+
121
+ `rich-ri --server` starts RDoc's documentation web server on port 8214;
122
+ `--server=PORT` selects another port. This mode requires the `webrick` gem:
123
+
124
+ ```sh
125
+ gem install webrick
126
+ ```
127
+
128
+ The server uses RDoc's web interface and network defaults, including listening
129
+ on all interfaces. Stop it with Ctrl-C. rich-ri's terminal themes do not apply
130
+ to the web pages.
131
+
132
+ `--profile` runs Ruby's profiler while reading documentation and prints timing
133
+ information when the command exits. It requires the `profile` gem:
134
+
135
+ ```sh
136
+ gem install profile
137
+ ```
138
+
139
+ Install either gem for the same Ruby that runs rich-ri. Neither is needed for
140
+ normal terminal lookup, highlighting or completion. Missing optional gems produce
141
+ an installation hint and exit status 1. When running through Bundler, add the
142
+ needed gem to that bundle too.
143
+
144
+ ## Exit status
145
+
146
+ The command exits with 0 on success, 1 for lookup or usage errors and 130 when
147
+ interrupted. A closed output pipe is a normal exit.
data/exe/rich-ri ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "rich_ri"
5
+
6
+ exit RichRI::CLI.run(ARGV)
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RichRI
4
+ SGR = /\e\[[\d;]*m/
5
+ RESET = "\e[0m"
6
+ COLORS = {
7
+ title: "1;36", heading: "1;34", subheading: "1;35",
8
+ code: "36", reference: "36", link: "4;36", label: "1;33",
9
+ muted: "90", emphasis: "3", bold: "1", strike: "9",
10
+ keyword: "35", string: "32", number: "33", constant: "33",
11
+ symbol: "33", method: "36", comment: "90", operator: "35"
12
+ }.freeze
13
+
14
+ def self.plain(text)
15
+ text.gsub(SGR, "")
16
+ end
17
+
18
+ # Documentation may contain literal terminal controls. Show them as text;
19
+ # only styles produced by this reader may reach the terminal as escapes.
20
+ def self.sanitize(text)
21
+ text.gsub(/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f\u0080-\u009f\u202a-\u202e\u2066-\u2069]|\r(?!\n)/) do |char|
22
+ format("\\u%04x", char.ord)
23
+ end
24
+ end
25
+
26
+ def self.width(text)
27
+ Reline::Unicode.calculate_width(text, true)
28
+ end
29
+
30
+ # Each word has balanced SGRs: less resets colors at newlines, and wrapping
31
+ # must never make a style bleed into the next paragraph or the shell prompt.
32
+ def self.paint(text, *roles, enabled: true)
33
+ Theme.new.paint(text, *roles, enabled:)
34
+ end
35
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RichRI
4
+ # Optional highlighting must not prevent the reader from displaying a page.
5
+ class Bat
6
+ TIMEOUT = 2
7
+ MAX_INPUT = 1_048_576
8
+ MAX_OUTPUT = 8_388_608
9
+
10
+ def initialize(timeout: TIMEOUT, max_output: MAX_OUTPUT)
11
+ @timeout = timeout
12
+ @max_output = max_output
13
+ @available = true
14
+ end
15
+
16
+ def highlight(text, language:, theme:)
17
+ return unless @available && text.bytesize <= MAX_INPUT
18
+
19
+ output = capture(text, language, theme)
20
+ output&.force_encoding(text.encoding)
21
+ output = output.delete_suffix("\n") if output && !text.end_with?("\n") && output.end_with?("\n")
22
+ return output if output&.valid_encoding? && RichRI.plain(output) == text
23
+
24
+ @available = false
25
+ nil
26
+ rescue SystemCallError, IOError, Timeout::Error
27
+ @available = false
28
+ nil
29
+ end
30
+
31
+ private
32
+
33
+ def capture(text, language, theme)
34
+ args = ["bat", "--no-config", "--language=#{language}", "--style=plain", "--color=always",
35
+ "--paging=never", "--wrap=never", "--theme=#{theme}"]
36
+ Open3.popen2(*args, err: File::NULL, pgroup: true) do |input, output, waiter|
37
+ completed = false
38
+ writer = Thread.new { write(input, text) }
39
+ Timeout.timeout(@timeout) do
40
+ result = output.read(@max_output + 1).to_s
41
+ next if result.bytesize > @max_output
42
+
43
+ writer.value
44
+ status = waiter.value
45
+ completed = true
46
+ result if status.success?
47
+ end
48
+ ensure
49
+ writer&.kill
50
+ terminate(waiter) unless completed
51
+ end
52
+ end
53
+
54
+ def write(input, text)
55
+ input.write(text)
56
+ rescue Errno::EPIPE, IOError
57
+ nil
58
+ ensure
59
+ input.close unless input.closed?
60
+ end
61
+
62
+ def terminate(waiter)
63
+ Process.kill("TERM", -waiter.pid)
64
+ waiter.join(0.1)
65
+ # Descendants may still hold pipes open after their parent exits.
66
+ Process.kill("KILL", -waiter.pid)
67
+ rescue Errno::ESRCH
68
+ nil
69
+ ensure
70
+ waiter.join
71
+ end
72
+ end
73
+ end