Skip to content

Commit 6b6874b

Browse files
authored
Display command description in doc dialog on tab completion (#1180)
* Refactor show_doc_dialog_proc into smaller methods * Display command description in doc dialog on tab completion * Consolidate full-screen doc display into display_document The open_doc paths in dialog content methods duplicated logic already in display_document. Now the dialog lambda calls display_document directly for Alt+d, and the content methods only handle inline dialog rendering. * Preserve whitespace alignment in wrap_lines Split on /(\s+)/ instead of /\s+/ so multi-space gaps in help text (e.g. option alignment) are preserved when wrapping lines.
1 parent 209bcf7 commit 6b6874b

6 files changed

Lines changed: 309 additions & 115 deletions

File tree

lib/irb/command/base.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,46 @@ def execute(irb_context, arg)
3737
puts e.message
3838
end
3939

40+
# Returns formatted lines for display in the doc dialog popup.
41+
def doc_dialog_content(name, width)
42+
lines = []
43+
lines << Color.colorize(name, [:BOLD, :BLUE]) + Color.colorize(" (command)", [:CYAN])
44+
lines << ""
45+
lines.concat(wrap_lines(description, width))
46+
if help_message
47+
lines << ""
48+
lines.concat(wrap_lines(help_message, width))
49+
end
50+
lines
51+
end
52+
4053
private
4154

4255
def highlight(text)
4356
Color.colorize(text, [:BOLD, :BLUE])
4457
end
58+
59+
def wrap_lines(text, width)
60+
text.lines.flat_map do |line|
61+
line = line.chomp
62+
next [''] if line.empty?
63+
next [line] if line.length <= width
64+
65+
indent = line[/\A\s*/]
66+
parts = line.strip.split(/(\s+)/)
67+
result = []
68+
current = indent.dup
69+
parts.each do |part|
70+
if current != indent && current.length + part.length > width
71+
result << current.rstrip
72+
current = indent.dup
73+
end
74+
current << part unless current == indent && part.match?(/\A\s+\z/)
75+
end
76+
result << current.rstrip unless current == indent
77+
result
78+
end
79+
end
4580
end
4681

4782
def initialize(irb_context)

lib/irb/completion.rb

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,29 @@
88
require_relative 'ruby-lex'
99

1010
module IRB
11+
class DocumentTarget # :nodoc:
12+
attr_reader :name
13+
14+
def initialize(name)
15+
@name = name
16+
end
17+
end
18+
19+
class CommandDocument < DocumentTarget # :nodoc:
20+
end
21+
22+
# Represents a method/class documentation target. May hold multiple names
23+
# when the receiver is ambiguous (e.g. `{}.any?` could be Hash#any? or Proc#any?).
24+
# The dialog popup uses only the first name; the full-screen display renders all.
25+
class MethodDocument < DocumentTarget # :nodoc:
26+
attr_reader :names
27+
28+
def initialize(*names)
29+
super(names.first)
30+
@names = names
31+
end
32+
end
33+
1134
class BaseCompletor # :nodoc:
1235

1336
# Set of reserved words used by Ruby, you should not use these for
@@ -76,6 +99,12 @@ def command_candidates(target)
7699
end
77100
end
78101

102+
def command_document_target(preposing, matched)
103+
if preposing.empty? && IRB::Command.command_names.include?(matched)
104+
CommandDocument.new(matched)
105+
end
106+
end
107+
79108
def retrieve_files_to_require_relative_from_current_dir
80109
@files_from_current_dir ||= Dir.glob("**/*.{rb,#{RbConfig::CONFIG['DLEXT']}}", base: '.').map { |path|
81110
path.sub(/\.(rb|#{RbConfig::CONFIG['DLEXT']})\z/, '')
@@ -118,8 +147,10 @@ def completion_candidates(preposing, target, _postposing, bind:)
118147
end
119148

120149
def doc_namespace(preposing, matched, _postposing, bind:)
121-
result = ReplTypeCompletor.analyze(preposing + matched, binding: bind, filename: @context.irb_path)
122-
result&.doc_namespace('')
150+
command_document_target(preposing, matched) || begin
151+
result = ReplTypeCompletor.analyze(preposing + matched, binding: bind, filename: @context.irb_path)
152+
result&.doc_namespace('')
153+
end
123154
end
124155
end
125156

@@ -201,8 +232,8 @@ def completion_candidates(preposing, target, postposing, bind:)
201232
commands | completion_data
202233
end
203234

204-
def doc_namespace(_preposing, matched, _postposing, bind:)
205-
retrieve_completion_data(matched, bind: bind, doc_namespace: true)
235+
def doc_namespace(preposing, matched, _postposing, bind:)
236+
command_document_target(preposing, matched) || retrieve_completion_data(matched, bind: bind, doc_namespace: true)
206237
end
207238

208239
def retrieve_completion_data(input, bind:, doc_namespace:)

0 commit comments

Comments
 (0)