tkri 0.9.3 → 0.9.4

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 (2) hide show
  1. data/lib/tkri.rb +178 -67
  2. metadata +2 -2
@@ -44,7 +44,7 @@ module Tkri
44
44
  'mswin' => 'qri.bat -f ansi "%s" 2>&1',
45
45
  }
46
46
 
47
- TAGS = {
47
+ VISUALS = {
48
48
  # The '__base__' attibutes are applied for every textfield and textarea.
49
49
  # Set the background color and font to whatever you like.
50
50
  #
@@ -53,14 +53,16 @@ module Tkri
53
53
  # name (i.e., one of: 'courier', 'times', 'helvetica') at the end to serve
54
54
  # as a fallback.
55
55
  '__base__' => { :background => '#ffeeff', :font => { :family => ['Bitstream Vera Sans Mono', 'Menlo', 'Monaco', 'Courier'], :size => 10 } },
56
- 'bold' => { :foreground => 'blue' },
57
- 'italic' => { :foreground => '#6b8e23' }, # greenish
58
- 'code' => { :foreground => '#1874cd' }, # blueish
59
- 'header2' => { :background => '#ffe4b5', :font => { :family => ['Geneva', 'Arial', 'Helvetica'], :size => 12 } },
60
- 'header3' => { :background => '#ffe4b5', :font => { :family => ['Geneva', 'Arial', 'Helvetica'], :size => 14 } },
61
- 'keyword' => { :foreground => 'red' },
62
- 'search' => { :background => 'yellow' },
63
- 'hidden' => { :elide => true },
56
+ 'bold' => { :foreground => 'blue', :is_tag => true },
57
+ 'italic' => { :foreground => '#6b8e23', :is_tag => true }, # greenish
58
+ 'code' => { :foreground => '#1874cd', :is_tag => true }, # blueish
59
+ 'header2' => { :background => '#ffe4b5', :font => { :family => ['Geneva', 'Arial', 'Helvetica'], :size => 12 }, :is_tag => true },
60
+ 'header3' => { :background => '#ffe4b5', :font => { :family => ['Geneva', 'Arial', 'Helvetica'], :size => 14 }, :is_tag => true },
61
+ 'keyword' => { :foreground => 'red', :is_tag => true },
62
+ 'search' => { :background => 'yellow', :is_tag => true },
63
+ 'hidden' => { :elide => true, :is_tag => true},
64
+ 'tab_button' => { :padx => 10, :font => { :family => ['Bitstream Vera Sans Mono', 'Menlo', 'Monaco', 'Courier'], :size => 10 } },
65
+ 'go_button' => { :padx => 20 },
64
66
  }
65
67
 
66
68
  BINDINGS = {
@@ -113,6 +115,8 @@ module Tkri
113
115
  # History.
114
116
  'b1013' => { :key => 'ButtonRelease-3', :source => 'info', :command => 'interactive_history_back' },
115
117
  'b1014' => { :key => 'Key-BackSpace', :source => 'info', :command => 'interactive_history_back', :cancel_default => true },
118
+ 'b1014b' => { :key => 'Alt-Key-Left', :source => 'root', :command => 'interactive_history_back', :cancel_default => true },
119
+ 'b1014c' => { :key => 'Alt-Key-Right', :source => 'root', :command => 'interactive_history_forward', :cancel_default => true },
116
120
 
117
121
  # Tk doesn't support read-only rext widgets. So for every "ascii" global binding we also
118
122
  # need to duplicate it on the 'info' widget, :cancel_default'ing it.
@@ -142,7 +146,7 @@ module Tkri
142
146
  f.puts "# You may erase any setting in this file for which you want to use"
143
147
  f.puts "# the default value."
144
148
  f.puts "#"
145
- f.puts({ 'command' => COMMAND, 'tags' => TAGS, 'bindings' => BINDINGS }.to_yaml)
149
+ f.puts({ 'command' => COMMAND, 'visuals' => VISUALS, 'bindings' => BINDINGS }.to_yaml)
146
150
  end
147
151
  end
148
152
 
@@ -154,7 +158,7 @@ module Tkri
154
158
 
155
159
  module Settings
156
160
  COMMAND = DefaultSettings::COMMAND.dup
157
- TAGS = DefaultSettings::TAGS.dup
161
+ VISUALS = DefaultSettings::VISUALS.dup
158
162
  BINDINGS = DefaultSettings::BINDINGS
159
163
 
160
164
  # Load the settings from the 'rc' file. We merge them into the existing settings.
@@ -164,33 +168,84 @@ module Tkri
164
168
  settings = YAML.load_file(Tkri.get_rc_file_path)
165
169
  if settings.instance_of? Hash
166
170
  COMMAND.merge!(settings['command']) if settings['command']
167
- TAGS.merge!(settings['tags']) if settings['tags']
171
+ VISUALS.merge!(settings['visuals']) if settings['visuals']
168
172
  BINDINGS.merge!(settings['bindings']) if settings['bindings']
169
173
  end
170
174
  end
171
175
  end
176
+
177
+ # get_configuration() converts any of the VISUAL hashes, above, to a hash suitable
178
+ # for use in Tk. Corrently, it only converts the :font attribute to a TkFont instance.
179
+ def self.get_configuration(name)
180
+ h = VISUALS[name].dup
181
+ h.delete(:is_tag)
182
+ if h[:font].instance_of? Hash
183
+ h[:font] = h[:font].dup
184
+ if h[:font][:family]
185
+ availables = TkFont.families.map { |s| s.downcase }
186
+ desired = Array(h[:font][:family]).map { |s| s.downcase }
187
+ # Select the first family available on this system.
188
+ h[:font][:family] = (desired & availables).first || 'courier'
189
+ end
190
+ h[:font] = TkFont.new(h[:font])
191
+ end
192
+ return h
193
+ end
172
194
  end
173
195
 
174
196
  HistoryEntry = Struct.new(:topic, :cursor, :yview)
175
197
 
176
- # hash_to_configuration() converts any of the TAG hashes, above, to a hash suitable
177
- # for use in Tk. Corrently, it only converts the :font attribute to a TkFont instance.
178
- def self.hash_to_configuration(hash)
179
- ret = hash.dup
180
- if ret[:font].instance_of? Hash
181
- ret[:font] = ret[:font].dup
182
- if ret[:font][:family]
183
- availables = TkFont.families.map { |s| s.downcase }
184
- desired = Array(ret[:font][:family]).map { |s| s.downcase }
185
- # Select the first family available on this system.
186
- ret[:font][:family] = (desired & availables).first || 'courier'
187
- end
188
- ret[:font] = TkFont.new(ret[:font])
189
- end
190
- return ret
198
+ class History
199
+ def initialize
200
+ @arr = []
201
+ @current = -1
202
+ end
203
+
204
+ def size
205
+ @arr.size
206
+ end
207
+
208
+ def current
209
+ if @current >= 0
210
+ @arr[@current]
211
+ else
212
+ nil
213
+ end
214
+ end
215
+
216
+ def back
217
+ if @current > 0
218
+ @current -= 1
219
+ end
220
+ current
221
+ end
222
+
223
+ def foreward
224
+ if @current < @arr.size - 1
225
+ @current += 1
226
+ end
227
+ end
228
+
229
+ def add(entry)
230
+ @current += 1
231
+ @arr[@current] = entry
232
+ # Adding an entry removes all entries in the "future".
233
+ @arr.slice!(@current + 1, @arr.size)
234
+ end
235
+
236
+ def at_beginning
237
+ @current <= 0
238
+ end
239
+
240
+ def at_end
241
+ @current == @arr.size - 1;
242
+ end
191
243
  end
192
244
 
193
245
  # Attachs Settings::BINDINGS to a certain widget.
246
+ #
247
+ # Ideally this should be a method of TkWidget, but for some reason widgets don't
248
+ # seem to inherit methods I define on TkWidget.
194
249
  def self.attach_bindings(widget, widget_id_string)
195
250
  Tkri::Settings::BINDINGS.each_pair { |ignored_key, b|
196
251
  if (b[:source] == widget_id_string)
@@ -229,13 +284,14 @@ class Tab < TkFrame
229
284
  addressbar = TkFrame.new(self) { |ab|
230
285
  pack :side => 'top', :fill => 'x'
231
286
  TkButton.new(ab) {
287
+ configure Settings::get_configuration('go_button')
232
288
  text 'Go'
233
289
  command { app.go }
234
290
  pack :side => 'right'
235
291
  }
236
292
  }
237
293
  @address = TkEntry.new(addressbar) {
238
- configure Tkri::hash_to_configuration(Settings::TAGS['__base__'])
294
+ configure Settings::get_configuration('__base__')
239
295
  configure :width => 30
240
296
  pack :side => 'left', :expand => true, :fill => 'both'
241
297
  }
@@ -245,7 +301,7 @@ class Tab < TkFrame
245
301
  #
246
302
  _frame = self
247
303
  @info = TkText.new(self) { |t|
248
- configure Tkri::hash_to_configuration(Settings::TAGS['__base__'])
304
+ configure Settings::get_configuration('__base__')
249
305
  pack :side => 'left', :fill => 'both', :expand => true
250
306
  TkScrollbar.new(_frame) { |s|
251
307
  pack :side => 'right', :fill => 'y'
@@ -254,18 +310,16 @@ class Tab < TkFrame
254
310
  }
255
311
  }
256
312
 
257
- Settings::TAGS.each do |name, hash|
258
- @info.tag_configure(name, Tkri::hash_to_configuration(hash))
313
+ Settings::VISUALS.each do |name, hash|
314
+ if hash[:is_tag]
315
+ @info.tag_configure(name, Settings::get_configuration(name))
316
+ end
259
317
  end
260
318
 
261
319
  Tkri.attach_bindings @address, 'addressbox'
262
320
  Tkri.attach_bindings @info, 'info'
263
321
 
264
- @history = []
265
- end
266
-
267
- def interactive_history_back e
268
- back
322
+ @history = History.new
269
323
  end
270
324
 
271
325
  def interactive_goto_topic_in_addressbox e
@@ -288,7 +342,7 @@ class Tab < TkFrame
288
342
  end
289
343
  end
290
344
 
291
- # It seeks RubyTk doesn't support the the getSelected method for Text widgets.
345
+ # It seems RubyTk doesn't support the the getSelected method for Text widgets.
292
346
  # So here's a method of our own to get the selection.
293
347
  def get_selection
294
348
  begin
@@ -510,22 +564,49 @@ class Tab < TkFrame
510
564
  end
511
565
 
512
566
  # Navigates to some topic.
513
- def go(topic=nil, skip_history=false)
567
+ def go(topic=nil)
514
568
  topic = (topic || @address.get).strip
515
569
  return if topic.empty?
516
- if @topic and not skip_history
517
- # Push current topic into history.
518
- @history << HistoryEntry.new(@topic, @info.index('insert'), @info.yview[0])
519
- end
520
570
  @topic = fixup_topic(topic)
521
- @app.status = 'Loading "%s"...' % @topic
571
+ # First, save the cursor position in the current history entry.
572
+ store_in_history
573
+ # Next, add a new entry to the history.
574
+ @history.add HistoryEntry.new(@topic, nil, nil)
575
+ # Finally, load the topic.
576
+ _load_topic @topic
577
+ end
578
+
579
+ # Call this method before switching to another topic. This
580
+ # method saves the cursor position in the history.
581
+ def store_in_history
582
+ if current = @history.current
583
+ current.cursor = @info.index('insert')
584
+ current.yview = @info.yview[0]
585
+ end
586
+ end
587
+
588
+ # Call this method after moving fack and forth in the history.
589
+ # This method restores the topic and cursor position recorded
590
+ # in the current history entry.
591
+ def restore_from_history
592
+ if current = @history.current
593
+ @topic = current.topic
594
+ _load_topic(topic) do
595
+ @info.yview_moveto current.yview
596
+ set_cursor current.cursor
597
+ end
598
+ end
599
+ end
600
+
601
+ def _load_topic(topic)
602
+ @app.status = 'Loading "%s"...' % topic
522
603
  @address.delete('0', 'end')
523
- @address.insert('end', @topic)
604
+ @address.insert('end', topic)
524
605
  focus_address
525
606
  # We need to give our GUI a chance to redraw itself, so we run the
526
607
  # time-consuming 'ri' command "in the next go".
527
608
  TkAfter.new 100, 1 do
528
- ri = @app.fetch_ri(@topic)
609
+ ri = @app.fetch_ri(topic)
529
610
  set_ansi_text(ri)
530
611
  @app.refresh_tabsbar
531
612
  @app.status = ''
@@ -534,14 +615,22 @@ class Tab < TkFrame
534
615
  yield if block_given?
535
616
  end.start
536
617
  end
618
+
619
+ # Navigate to the previous topic in history.
620
+ def interactive_history_back e
621
+ if not @history.at_beginning
622
+ store_in_history
623
+ @history.back
624
+ restore_from_history
625
+ end
626
+ end
537
627
 
538
- # Navigate to the previous topic viewed.
539
- def back
540
- if (entry = @history.pop)
541
- go(entry.topic, true) do
542
- @info.yview_moveto entry.yview
543
- set_cursor entry.cursor
544
- end
628
+ # Navigate to the next topic in history.
629
+ def interactive_history_forward e
630
+ if not @history.at_end
631
+ store_in_history
632
+ @history.foreward
633
+ restore_from_history
545
634
  end
546
635
  end
547
636
 
@@ -586,11 +675,13 @@ class Tabsbar < TkFrame
586
675
  @buttons = []
587
676
  @tabs.each_with_index do |tab, i|
588
677
  b = TkButton.new(self, :text => (tab.topic || '<new>')).pack :side => 'left'
678
+ b.configure Settings::get_configuration('tab_button')
589
679
  b.command { set_current_tab_by_index i }
590
680
  Tkri.attach_bindings b, 'tabbutton'
591
681
  @buttons << b
592
682
  end
593
- plus = TkButton.new(self, :text => '+').pack :side => 'left'
683
+ plus = TkButton.new(self, :text => '+').pack :side => 'left', :padx => 10
684
+ plus.configure Settings::get_configuration('tab_button')
594
685
  plus.command { @tabs.new_tab }
595
686
  @buttons << plus
596
687
  set_current_tab_by_index @tabs.current_tab_as_index
@@ -621,7 +712,6 @@ class Tabs < TkFrame
621
712
  tab.focus_address
622
713
  @tabs << tab
623
714
  set_current_tab_by_index(@tabs.size - 1, true)
624
- # @app.refresh_tabsbar
625
715
  end
626
716
 
627
717
  def interactive_new_tab e
@@ -844,10 +934,22 @@ class App
844
934
  if text == "nil\n"
845
935
  text = 'Topic "%s" not found.' % topic
846
936
  end
937
+ text = _post_process_text(text)
847
938
  @ri_cache[topic] = text
848
939
  @cached_topics << topic
849
940
  end
850
941
 
942
+ # Remove the oldest topic from the cache
943
+ if @cached_topics.length > 20
944
+ @ri_cache.delete @cached_topics.shift
945
+ end
946
+
947
+ return text
948
+ end
949
+
950
+ # Enhance the ri text a bit.
951
+ def _post_process_text(text)
952
+
851
953
  # Make the "Multiple choices" output easier to read.
852
954
  if text.match /Multiple choices/
853
955
  text.gsub! /,\s+/, "\n"
@@ -855,18 +957,27 @@ class App
855
957
  text.gsub! /\n/, "\n "
856
958
  end
857
959
 
858
- # Remove the oldest topic from the cache
859
- if @cached_topics.length > 20
860
- @ri_cache.delete @cached_topics.shift
861
- end
862
-
960
+ # Highlight the names of included modules.
961
+ bold_on = "\x1b[1m"
962
+ bold_off = "\x1b[0m"
963
+ parts = text.split /([\r\n]\S*Includes:.*?[\r\n][\r\n])/m
964
+ parts.map! { |s|
965
+ if s =~ /[\r\n]\S*Includes:/
966
+ # Modules have parentheses following them.
967
+ s.gsub! /(\S+)\(/, bold_on + '\1' + bold_off + '('
968
+ else
969
+ s
970
+ end
971
+ }
972
+ text = parts.join
973
+
863
974
  return text
864
975
  end
865
976
 
866
977
  def helpbox(title, text)
867
978
  w = TkToplevel.new(:title => title)
868
979
  t = TkText.new(w, :height => text.count("\n"), :width => 80).pack.insert('1.0', text)
869
- t.configure Tkri::hash_to_configuration(Settings::TAGS['__base__'])
980
+ t.configure Settings::get_configuration('__base__')
870
981
  TkButton.new(w, :text => 'Close', :command => proc { w.destroy }).pack
871
982
  end
872
983
 
@@ -900,16 +1011,18 @@ Ctrl + Left mouse button
900
1011
  Navigate to the topic selected (marked) with the mouse.
901
1012
  Middle mouse button
902
1013
  Navigate to the topic under the cursor. Opens in a new tab.
903
- Right mouse button
904
- Move back in the history.
1014
+ Right mouse button, Backspace, Alt+Left
1015
+ Move back in the history.
1016
+ Alt+Right
1017
+ Move foreward in the history.
905
1018
  Ctrl+W. Or middle mouse button, on a tab button
906
1019
  Close the tab (unless this is the only tab).
907
1020
  Ctrl+L
908
1021
  Move the keyboard focus to the "address" box, where you can type a topic.
909
1022
  Ctrl+T
910
1023
  New tab.
911
- Alt-1 .. Alt-9, Ctrl-PgUp, Ctrl-PgDn
912
- Swith to a certain, or to the next/previous one.
1024
+ Alt+1 .. Alt+9, Ctrl+PgUp, Ctrl+PgDn
1025
+ Swith to a certain tab, or to the next/previous one.
913
1026
  u
914
1027
  Goes "up" one level. That is, if you're browsing Module::Class#method,
915
1028
  you'll be directed to Module::Class. Press 'u' again for Module.
@@ -983,8 +1096,6 @@ Here's a list of known issues / bugs:
983
1096
 
984
1097
  ON THE WINDOWS PLATFORM:
985
1098
 
986
- * Tkri looks ugly.
987
-
988
1099
  * The mouse wheel works only if the keyboard focus is in the
989
1100
  textarea. That's unfortunate. It's a Tk issue, not Tkri's.
990
1101
 
@@ -994,7 +1105,7 @@ ON THE WINDOWS PLATFORM:
994
1105
 
995
1106
  ALL PLATFORMS:
996
1107
 
997
- * There's a 'backward' command, but no 'forward' command.
1108
+ * No known issues.
998
1109
  EOS
999
1110
  end
1000
1111
  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.3
4
+ version: 0.9.4
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-10-29 00:00:00 +02:00
12
+ date: 2009-10-30 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency