tkri 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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