tkri 0.9.0 → 0.9.1

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.
Files changed (3) hide show
  1. data/bin/tkri +15 -2
  2. data/lib/tkri.rb +241 -54
  3. metadata +7 -5
data/bin/tkri CHANGED
@@ -1,9 +1,22 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'tkri'
3
+ begin
4
+ # If we were installed using the Gem system:
5
+ require 'tkri'
6
+ rescue LoadError => load_error
7
+ # No, we weren't installed as a Gem. We need to make Ruby aware
8
+ # of our library folder.
9
+ lib = File.join(File.dirname(__FILE__), '..', 'lib')
10
+ $: << lib
11
+ require 'tkri'
12
+ end
4
13
 
5
14
  app = Tkri::App.new
6
15
  ARGV.each do |topic|
7
- app.go topic, true
16
+ if topic == '--dump-rc'
17
+ Tkri::DefaultSettings.dump
18
+ else
19
+ app.go topic, true
20
+ end
8
21
  end
9
22
  app.run
@@ -10,28 +10,117 @@ require 'tk'
10
10
 
11
11
  module Tkri
12
12
 
13
- COMMAND = {
14
- # Each platform may use a different command. The commands are indexed by any
15
- # substring in RUBY_PLATFORM. If none matches the platform, the 'default' key
16
- # is used.
17
- 'default' => 'qri -f ansi "%s"',
18
- /linux/ => 'qri -f ansi "%s" 2>&1',
19
- /darwin/ => 'qri -f ansi "%s" 2>&1',
20
- }
21
-
22
- TAGS = {
23
- 'bold' => { :foreground => 'blue' },
24
- 'italic' => { :foreground => '#6b8e23' }, # greenish
25
- 'code' => { :foreground => '#1874cd' }, # blueish
26
- 'header2' => { :background => '#ffe4b5' },
27
- 'header3' => { :background => '#ffe4b5' },
28
- 'keyword' => { :foreground => 'red' },
29
- 'search' => { :background => 'yellow' },
30
- 'hidden' => { :elide => true },
31
- }
13
+ # Returns the pathname of the 'rc' file.
14
+ def self.get_rc_file_path
15
+ basename = RUBY_PLATFORM.index('mswin') ? '_tkrirc' : '.tkrirc'
16
+ if ENV['HOME']
17
+ File.join(ENV['HOME'], basename)
18
+ else
19
+ # Probably Windows with no $HOME set. Dir.pwd sucks but is there
20
+ # anything better to do? At least the "Help :: About the RC file"
21
+ # screen displays this value, so the user won't be completely clueless...
22
+ File.join(Dir.pwd, basename)
23
+ end
24
+ end
25
+
26
+ module DefaultSettings
27
+
28
+ COMMAND = {
29
+ # Each platform may use a different command. The commands are indexed by any
30
+ # substring in RUBY_PLATFORM. If none matches the platform, the '__default__' key
31
+ # is used.
32
+ '__default__' => 'qri -f ansi "%s"',
33
+
34
+ # The "2>&1" thingy tells UNIX shells to print any error messages to the standard output.
35
+ # This makes it possible to see, inside Tkri, the errors `qri' (or 'ri') happens to emmit
36
+ # (this happens seldom, due to bugs in `qri', so it's not a very critical feature).
37
+ 'linux' => 'qri -f ansi "%s" 2>&1',
38
+ 'darwin' => 'qri -f ansi "%s" 2>&1',
39
+
40
+ # And here's the command for MS-Windows.
41
+ # It turns out Windows' CMD.EXE too supports "2>&1". This shell is used on NT-based
42
+ # systems. (In other words, if you're using the good old Windows 98, you'll have to remove
43
+ # this "2>&1").
44
+ 'mswin' => 'qri.bat -f ansi "%s" 2>&1',
45
+ }
46
+
47
+ TAGS = {
48
+ # The '__base__' attibutes are applied for every textfield and textarea.
49
+ # Set the background color and font to whatever you like.
50
+ #
51
+ # Font families are designated by an ordered array of names. The first
52
+ # found on the system will be used. So make sure to put a generic family
53
+ # name (i.e., one of: 'courier', 'times', 'helvetica') at the end to serve
54
+ # as a fallback.
55
+ '__base__' => { :background => '#ffeeff', :font => { :family => ['Bitstream Vera Sans Mono', 'courier'], :size => 10 } },
56
+ 'bold' => { :foreground => 'blue' },
57
+ 'italic' => { :foreground => '#6b8e23' }, # greenish
58
+ 'code' => { :foreground => '#1874cd' }, # blueish
59
+ 'header2' => { :background => '#ffe4b5', :font => { :family => ['helvetica'], :size => 16 } },
60
+ 'header3' => { :background => '#ffe4b5', :font => { :family => ['helvetica'], :size => 16 } },
61
+ 'keyword' => { :foreground => 'red' },
62
+ 'search' => { :background => 'yellow' },
63
+ 'hidden' => { :elide => true },
64
+ }
65
+
66
+ # Dump these settings into an 'rc' file.
67
+ def self.dump
68
+ require 'yaml'
69
+ open(Tkri.get_rc_file_path, 'w') do |f|
70
+ f.puts "#"
71
+ f.puts "# The documentation for these settings can be found in this file:"
72
+ f.puts "# " + File.expand_path(__FILE__)
73
+ f.puts "# Also, see the 'About the `rc` file' under the 'Help' menu."
74
+ f.puts "#"
75
+ f.puts "# You may erase any setting in this file for which you want to use"
76
+ f.puts "# the default value."
77
+ f.puts "#"
78
+ f.puts({ 'command' => COMMAND, 'tags' => TAGS }.to_yaml)
79
+ end
80
+ end
81
+
82
+ end # module DefaultSettings
83
+
84
+ module Settings
85
+ COMMAND = DefaultSettings::COMMAND.dup
86
+ TAGS = DefaultSettings::TAGS.dup
87
+
88
+ # Load the settings from the 'rc' file. We merge them into the existing settings.
89
+ def self.load
90
+ if File.exist? Tkri.get_rc_file_path
91
+ require 'yaml'
92
+ settings = YAML.load_file(Tkri.get_rc_file_path)
93
+ if settings.instance_of? Hash
94
+ COMMAND.merge!(settings['command']) if settings['command']
95
+ TAGS.merge!(settings['tags']) if settings['tags']
96
+ end
97
+ end
98
+ end
99
+ end
32
100
 
33
101
  HistoryEntry = Struct.new(:topic, :cursor, :yview)
34
102
 
103
+ # hash_to_configuration() converts any of the TAG hashes, above, to a hash suitable
104
+ # for use in Tk. Corrently, it only converts the :font attribute to a TkFont instance.
105
+ def self.hash_to_configuration(hash)
106
+ ret = hash.dup
107
+ if ret[:font].instance_of? Hash
108
+ if ret[:font][:family]
109
+ ret[:font] = ret[:font].dup
110
+ availables = TkFont.families.map { |s| s.downcase }
111
+ # Select the first family available on this system.
112
+ Array(ret[:font][:family]).each { |family|
113
+ if availables.include? family.downcase
114
+ ret[:font][:family] = family
115
+ break
116
+ end
117
+ }
118
+ end
119
+ ret[:font] = TkFont.new(ret[:font])
120
+ end
121
+ return ret
122
+ end
123
+
35
124
  # A Tab encapsulates an @address box, where you type the topic to go to; a "Go"
36
125
  # button; and an @info box in which to show the topic.
37
126
  class Tab < TkFrame
@@ -54,7 +143,8 @@ class Tab < TkFrame
54
143
  }
55
144
  }
56
145
  @address = TkEntry.new(addressbar) {
57
- configure :font => 'courier', :width => 30
146
+ configure Tkri::hash_to_configuration(Settings::TAGS['__base__'])
147
+ configure :width => 30
58
148
  pack :side => 'left', :expand => true, :fill => 'both'
59
149
  }
60
150
 
@@ -63,6 +153,7 @@ class Tab < TkFrame
63
153
  #
64
154
  _frame = self
65
155
  @info = TkText.new(self) { |t|
156
+ configure Tkri::hash_to_configuration(Settings::TAGS['__base__'])
66
157
  pack :side => 'left', :fill => 'both', :expand => true
67
158
  TkScrollbar.new(_frame) { |s|
68
159
  pack :side => 'right', :fill => 'y'
@@ -71,7 +162,7 @@ class Tab < TkFrame
71
162
  }
72
163
  }
73
164
 
74
- TAGS.each do |name, conf|
165
+ Settings::TAGS.each do |name, conf|
75
166
  @info.tag_configure(name, conf)
76
167
  end
77
168
 
@@ -82,6 +173,8 @@ class Tab < TkFrame
82
173
  # If I make the following "ButtonRelease-2" instead, the <PasteSelection>
83
174
  # cancellation that follows won't work. Strange.
84
175
  @info.bind('Button-2') { |e| go_xy_word(e.x, e.y, true) }
176
+ @info.bind('Key-Return') { go_caret_word(); break }
177
+ @info.bind('Key-KP_Enter') { go_caret_word(); break }
85
178
  @info.bind('ButtonRelease-3') { |e| back }
86
179
  @info.bind('Key-BackSpace') { |e| back; break }
87
180
 
@@ -165,7 +258,16 @@ class Tab < TkFrame
165
258
  # upon releasing the mouse button.)
166
259
  return
167
260
  end
168
- if (word = get_xy_word(x, y))
261
+ go_word('@' + x.to_s + ',' + y.to_s, newtab)
262
+ end
263
+
264
+ # Navigate to the topic mentioned under the caret.
265
+ def go_caret_word(newtab=false)
266
+ go_word('insert', newtab)
267
+ end
268
+
269
+ def go_word(position, newtab=false)
270
+ if (word = get_word(position))
169
271
  @app.go word, newtab
170
272
  end
171
273
  end
@@ -184,15 +286,22 @@ class Tab < TkFrame
184
286
  return ret[0].empty? ? nil : ret[2]
185
287
  end
186
288
 
187
- # Get the "topic" under the mouse cursor.
188
- def get_xy_word(x,y)
189
- cursor = '@' + x.to_s + ',' + y.to_s
190
- line = @info.get(cursor + ' linestart', cursor + ' lineend')
191
- pos = @info.get(cursor + ' linestart', cursor).length
289
+ # Get the "topic" at a certain postion.
290
+ #
291
+ # The 'position' paramter is an expression that can be, e.g., "insert"
292
+ # for the current caret position; or "@x,y" for the mouse poisition.
293
+ def get_word(position)
294
+ line = @info.get(position + ' linestart', position + ' lineend')
295
+ pos = @info.get(position + ' linestart', position).length
192
296
 
193
297
  line = ' ' + line + ' '
194
298
  pos += 1
195
299
 
300
+ if line[pos,1] == ' '
301
+ # If the user clicks a space between words, or after end of line, abort.
302
+ return nil
303
+ end
304
+
196
305
  a = pos
197
306
  a -= 1 while line[a-1,1] !~ /[ (]/
198
307
  z = pos
@@ -209,20 +318,20 @@ class Tab < TkFrame
209
318
  end
210
319
 
211
320
  a -= 1 # Undo the `line = ' ' + line` we did previously.
212
- @info.tag_add('keyword', '%s linestart + %d chars' % [ cursor, a ],
213
- '%s linestart + %d chars' % [ cursor, a+word.length ])
321
+ @info.tag_add('keyword', '%s linestart + %d chars' % [ position, a ],
322
+ '%s linestart + %d chars' % [ position, a+word.length ])
214
323
  word.strip!
215
324
 
216
325
  return nil if word.empty?
217
326
  return nil if word =~ /^-+$/ # A special case: a line of '-----'
218
327
 
219
- case get_previous_header(cursor)
328
+ case get_previous_header(position)
220
329
  when 'Instance methods:'
221
330
  word = topic + '#' + word
222
331
  when 'Class methods:'
223
332
  word = topic + '::' + word
224
333
  when 'Includes:'
225
- word = get_previous_class(cursor) + '#' + word if not word =~ /^[A-Z]/
334
+ word = get_previous_class(position) + '#' + word if not word =~ /^[A-Z]/
226
335
  end
227
336
 
228
337
  return word
@@ -429,6 +538,8 @@ class App
429
538
  def initialize
430
539
  @root = root = TkRoot.new { title 'Tkri' }
431
540
  @search_word = nil
541
+
542
+ Settings.load
432
543
 
433
544
  menu_spec = [
434
545
  [['File', 0],
@@ -442,8 +553,10 @@ class App
442
553
  # The following :menu_name=>'help' has no effect, but it should have...
443
554
  # probably a bug in RubyTK.
444
555
  [['Help', 0, { :menu_name => 'help' }],
445
- ['General', proc { help_general }, 0],
446
- ['Key bindings', proc { help_key_bindings }, 0]],
556
+ ['Overview', proc { help_overview }, 0],
557
+ ['Key bindings', proc { help_key_bindings }, 0],
558
+ ['Tips and tricks', proc { help_tips_and_tricks }, 0],
559
+ ['About the $HOME/.tkrirc file', proc { help_rc }, 0]],
447
560
  ]
448
561
  TkMenubar.new(root, menu_spec).pack(:side => 'top', :fill => 'x')
449
562
 
@@ -507,12 +620,14 @@ class App
507
620
  def search
508
621
  self.status = 'Type the string to search'
509
622
  entry = TkEntry.new(@root).pack(:fill => 'x').focus
510
- entry.bind('Key-Return') {
511
- self.status = ''
512
- @search_word = entry.get
513
- @tabs.current.search_next_word entry.get
514
- entry.destroy
515
- }
623
+ ['Key-Return', 'Key-KP_Enter'].each do |event|
624
+ entry.bind(event) {
625
+ self.status = ''
626
+ @search_word = entry.get
627
+ @tabs.current.search_next_word entry.get
628
+ entry.destroy
629
+ }
630
+ end
516
631
  ['Key-Escape', 'FocusOut'].each do |event|
517
632
  entry.bind(event) {
518
633
  self.status = ''
@@ -533,41 +648,62 @@ class App
533
648
 
534
649
  return @ri_cache[topic] if @ri_cache[topic]
535
650
 
536
- command = COMMAND.select { |k,v| RUBY_PLATFORM.index(k) }.first.to_a[1] || COMMAND['default']
537
- ri = Kernel.`(command % topic) # `
651
+ command = Settings::COMMAND.select { |k,v| RUBY_PLATFORM.index(k) }.first.to_a[1] || Settings::COMMAND['__default__']
652
+ text = Kernel.`(command % topic) # `
538
653
  if $? != 0
539
- ri += "\n" + "ERROR: Failed to run the command '%s' (exit code: %d). Please make sure you have this command in your PATH.\n\nYou may wish to modify this program's source (%s) to update the command to something that works on your system." % [command % topic, $?, $0]
654
+ text += "\n" + "ERROR: Failed to run the command '%s' (exit code: %d). Please make sure you have this command in your PATH.\n\nYou may wish to modify this program's source (%s) to update the command to something that works on your system." % [command % topic, $?, $0]
540
655
  else
541
- if ri == "nil\n"
542
- ri = 'Topic "%s" not found.' % topic
656
+ if text == "nil\n"
657
+ text = 'Topic "%s" not found.' % topic
543
658
  end
544
- @ri_cache[topic] = ri
659
+ @ri_cache[topic] = text
545
660
  @cached_topics << topic
546
661
  end
547
662
 
663
+ # Make the "Multiple choices" output easier to read.
664
+ if text.match /Multiple choices/
665
+ text.gsub! /,\s+/, "\n"
666
+ text.gsub! /^ */, ""
667
+ text.gsub! /\n/, "\n "
668
+ end
669
+
548
670
  # Remove the oldest topic from the cache
549
- if @cached_topics.length > 10
671
+ if @cached_topics.length > 20
550
672
  @ri_cache.delete @cached_topics.shift
551
673
  end
552
674
 
553
- return ri
675
+ return text
554
676
  end
555
677
 
556
678
  def helpbox(title, text)
557
679
  w = TkToplevel.new(:title => title)
558
- TkText.new(w, :height => text.count("\n"), :width => 80).pack.insert('1.0', text)
680
+ t = TkText.new(w, :height => text.count("\n"), :width => 80).pack.insert('1.0', text)
681
+ t.configure Tkri::hash_to_configuration(Settings::TAGS['__base__'])
559
682
  TkButton.new(w, :text => 'Close', :command => proc { w.destroy }).pack
560
683
  end
561
-
562
- def help_general
563
- helpbox('Help: General', <<EOS)
564
- Tkri (pronounce TIK-ri) is a GUI fron-end to RI (actually, by default,
565
- to FastRI).
684
+
685
+ def help_overview
686
+ helpbox('Help: Overview', <<EOS)
687
+ ABOUT
688
+
689
+ Tkri (pronounce TIK-ri) is a GUI front-end to the 'ri', or
690
+ 'qri', executables. By default it uses 'qri', which is part
691
+ of the Fast-RI package.
692
+
693
+ Tkri displays the output of that program in a window where
694
+ each work is "hyperlinked".
695
+
696
+ USAGE
697
+
698
+ Launch tkri by typing 'tkri' at the operating system prompt. You
699
+ can provide a starting topic as an argument on the command line.
700
+ Inside the application, type the topic you wish to go to at the
701
+ address bar, or click on a word in the main text.
566
702
  EOS
567
703
  end
568
704
 
569
705
  def help_key_bindings
570
- helpbox('Help: key bindings', <<EOS)
706
+ helpbox('Help: Key bindings', <<EOS)
571
707
  Left mouse button
572
708
  Navigate to the topic under the cursor.
573
709
  Middle mouse button
@@ -578,6 +714,57 @@ Ctrl+W. Or right mouse button, on a tab button
578
714
  Close the tab (unless this is the only tab).
579
715
  Ctrl+L
580
716
  Move the keyboard focus to the "address" box, where you can type a topic.
717
+ /
718
+ Find string in page.
719
+ EOS
720
+ end
721
+
722
+ def help_tips_and_tricks
723
+ helpbox('Help: Tips and tricks', <<EOS)
724
+ Some random tips:
725
+
726
+ Type '/' to quickly highlight a string in the page. (If the
727
+ string happens to be off-screen, hit ENTER to jump to it.)
728
+
729
+ Ctrl+L is probably the fastest way to move the keyboard
730
+ focus to the "address box".
731
+
732
+ The references for "Hash", "Array" and "String" are the most
733
+ sought-after, so instead of typing their full name in the address
734
+ box you can just type the letters h, a or s, respectively.
735
+
736
+ You can type the topic(s) directly on the command line;
737
+ e.g., "tkri Array.flatten sort_by"
738
+
739
+ Left-clicking on a word doesn't yet send you to a new page. It's
740
+ *releasing* the button that sends you there. This makes it possible to
741
+ select pieces of code: left-click, then drag, then release; since some
742
+ text is now selected, Tkri figures out that's all you wanted.
743
+ EOS
744
+ end
745
+
746
+ def help_rc
747
+ helpbox('Help: RC', <<EOS)
748
+ Tkri has some settings. E.g., the colors and fonts it uses.
749
+
750
+ These settings are hard-coded in the source code. But you can
751
+ override them by having an 'rc' file in your home folder. On
752
+ your system this file is here:
753
+
754
+ #{Tkri.get_rc_file_path}
755
+
756
+ (If it's at a weird place, set your $HOME environment variable.)
757
+
758
+ Of course, you're a busy person and don't have time to write
759
+ this file from scratch. So you're going to tell Tkri to write
760
+ this file for you; When you type:
761
+
762
+ tkri --dump-rc
763
+
764
+ you're telling Tkri to dump all its default settings into that
765
+ file. Then edit this file to your liking using your text editor.
766
+ Finally, run tkri; it will automatically merge the settings from
767
+ this file onto the hard-coded ones.
581
768
  EOS
582
769
  end
583
770
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tkri
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mooffie
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-01-12 00:00:00 +02:00
12
+ date: 2009-10-26 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -34,8 +34,10 @@ files:
34
34
  - README
35
35
  - lib/tkri.rb
36
36
  - bin/tkri
37
- has_rdoc: false
37
+ has_rdoc: true
38
38
  homepage: http://rubyforge.org/projects/tkri/
39
+ licenses: []
40
+
39
41
  post_install_message:
40
42
  rdoc_options: []
41
43
 
@@ -56,9 +58,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
56
58
  requirements: []
57
59
 
58
60
  rubyforge_project: tkri
59
- rubygems_version: 1.3.1
61
+ rubygems_version: 1.3.3
60
62
  signing_key:
61
- specification_version: 2
63
+ specification_version: 3
62
64
  summary: GUI front-end to FastRI's or RI's executables.
63
65
  test_files: []
64
66