twterm 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 484c724ac71bd898288537ded6bdb4ba7abe4560
4
- data.tar.gz: ed536081055851cca20e82ae28fde3b0a1a7ab82
3
+ metadata.gz: a822edd0e5719ca957ab183b1859bd6ec01113ca
4
+ data.tar.gz: ecec2ab95f599a575226d0a3239f1b93ef63a4f9
5
5
  SHA512:
6
- metadata.gz: e6d52bbeaf7b332a561b3faecb0a9ddcef375bcf2ffbb0ca2eeaf1c4f6dcc1e0b0a7e252241a0ff6e35f34790ef687ea267183d102e2e822d1293d38db9a26cc
7
- data.tar.gz: 7b28a087c4ff51c27f48276fda91b2e3376110dbac0ec85aafc5cdf5138b5a7a782d6330af9092736b6d061f6d472c741b322722c1e47dfa4bdee6172d08f976
6
+ metadata.gz: 1608685c830f8f614bc6549b498c271b7851deb05ee2e7054b62857ff89dae64f0d17f9f3d1fc22338493644aa35c9d4beb78c7ee2f6054eecd939108255d216
7
+ data.tar.gz: f188471c3974d031c257b2a6fd9169926d1350b663d4b71ce4ee180d20e6d63a2a61657c1755e019c01ca38f1402493d386b53cb4e4e90b752051969e184ecf0
data/lib/twterm.rb CHANGED
@@ -17,7 +17,7 @@ require 'twterm/app'
17
17
  require 'twterm/auth'
18
18
  require 'twterm/client'
19
19
  require 'twterm/color_manager'
20
- require 'twterm/completion_mamanger'
20
+ require 'twterm/completion_manager'
21
21
  require 'twterm/config'
22
22
  require 'twterm/extensions/array'
23
23
  require 'twterm/extensions/curses/window'
data/lib/twterm/app.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'curses'
2
2
 
3
- require 'twterm/completion_mamanger'
3
+ require 'twterm/completion_manager'
4
4
  require 'twterm/direct_message_composer'
5
5
  require 'twterm/event/screen/resize'
6
6
  require 'twterm/repository/direct_message_repository'
@@ -22,7 +22,7 @@ module Twterm
22
22
  DATA_DIR = "#{ENV['HOME']}/.twterm".freeze
23
23
 
24
24
  def completion_manager
25
- @completion_mamanger ||= CompletionManager.new(self)
25
+ @completion_manager ||= CompletionManager.new(self)
26
26
  end
27
27
 
28
28
  def direct_message_composer
@@ -0,0 +1,25 @@
1
+ module Twterm
2
+ module Completer
3
+ class AbstractCompleter
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def basic_word_break_characters
9
+ " \t\n\"\\'`$><=;|&{("
10
+ end
11
+
12
+ def complete(_query)
13
+ raise NotImplementedError, '`complete` method must be implemented'
14
+ end
15
+
16
+ def completion_append_character
17
+ ' '
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :app
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ require 'twterm/completer/abstract_completer'
2
+
3
+ module Twterm
4
+ module Completer
5
+ class DefaultCompleter < AbstractCompleter
6
+ def complete(query)
7
+ if query.start_with?('#')
8
+ app.hashtag_repository.all
9
+ .map { |tag| "##{tag}" }
10
+ .select { |tag| tag.start_with?(query) }
11
+ elsif query.start_with?('@')
12
+ app.user_repository.all
13
+ .map { |user| "@#{user.screen_name}" }
14
+ .select { |name| name.start_with?(query) }
15
+ else
16
+ []
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ require 'twterm/completer/abstract_completer'
2
+
3
+ module Twterm
4
+ module Completer
5
+ class ScreenNameCompleter < AbstractCompleter
6
+ def complete(query)
7
+ app.user_repository.all
8
+ .map { |user| user.screen_name }
9
+ .select { |name| name.start_with?(query) }
10
+ end
11
+
12
+ def completion_append_character
13
+ ''
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,180 @@
1
+ require 'twterm/completer/abstract_completer'
2
+
3
+ module Twterm
4
+ module Completer
5
+ class SearchQueryCompleter < AbstractCompleter
6
+ def completion_append_character
7
+ ''
8
+ end
9
+
10
+ def complete(q)
11
+ possible_operators = possible_operators_for_query(q)
12
+
13
+ if q.empty?
14
+ operators
15
+ elsif q.start_with?('#')
16
+ app.hashtag_repository.all
17
+ .map { |tag| "##{tag} " }
18
+ .select { |tag| tag.start_with?(q) }
19
+ elsif q.start_with?('@')
20
+ app.user_repository.all
21
+ .map { |user| "@#{user.screen_name} " }
22
+ .select { |name| name.start_with?(q) }
23
+ elsif !possible_operators.empty?
24
+ possible_operators
25
+ elsif q.start_with?('-from:')
26
+ app.user_repository.all
27
+ .map { |user| "-from:#{user.screen_name} " }
28
+ .select { |name| name.start_with?(q) }
29
+ elsif q.start_with?('from:')
30
+ app.user_repository.all
31
+ .map { |user| "from:#{user.screen_name} " }
32
+ .select { |name| name.start_with?(q) }
33
+ elsif q.start_with?('-to:')
34
+ app.user_repository.all
35
+ .map { |user| "-to:#{user.screen_name} " }
36
+ .select { |name| name.start_with?(q) }
37
+ elsif q.start_with?('to:')
38
+ app.user_repository.all
39
+ .map { |user| "to:#{user.screen_name} " }
40
+ .select { |name| name.start_with?(q) }
41
+ elsif q.start_with?('filter:')
42
+ filters.map { |f| "filter:#{f} " }.select { |f| f.start_with?(q) }
43
+ elsif q.start_with?('-filter:')
44
+ filters.map { |f| "-filter:#{f} " }.select { |f| f.start_with?(q) }
45
+ elsif q.start_with?('lang:')
46
+ langs.map { |l| "lang:#{l} " }.select { |l| l.start_with?(q) }
47
+ elsif q.start_with?('-lang:')
48
+ langs.map { |l| "-lang:#{l} " }.select { |l| l.start_with?(q) }
49
+ elsif q.start_with?('list:')
50
+ app.list_repository.all
51
+ .map { |list| "list:#{list.full_name.sub('@', '')} " }
52
+ .select { |name| name.start_with?(q) }
53
+ else
54
+ []
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def filters
61
+ [
62
+ 'images',
63
+ 'links',
64
+ 'media',
65
+ 'native_video',
66
+ 'periscope',
67
+ 'retweets',
68
+ 'safe',
69
+ 'twimg',
70
+ 'vine',
71
+ ]
72
+ end
73
+
74
+ def langs
75
+ [
76
+ 'am',
77
+ 'ar',
78
+ 'bg',
79
+ 'bn',
80
+ 'bo',
81
+ 'ckb',
82
+ 'da',
83
+ 'de',
84
+ 'dv',
85
+ 'el',
86
+ 'en',
87
+ 'es',
88
+ 'et',
89
+ 'fa',
90
+ 'fi',
91
+ 'fr',
92
+ 'gu',
93
+ 'he',
94
+ 'hi',
95
+ 'ht',
96
+ 'hu',
97
+ 'hy',
98
+ 'id',
99
+ 'is',
100
+ 'it',
101
+ 'ja',
102
+ 'ka',
103
+ 'km',
104
+ 'kn',
105
+ 'ko',
106
+ 'lo',
107
+ 'lt',
108
+ 'lv',
109
+ 'ml',
110
+ 'mr',
111
+ 'my',
112
+ 'ne',
113
+ 'nl',
114
+ 'no',
115
+ 'or',
116
+ 'pa',
117
+ 'pl',
118
+ 'ps',
119
+ 'pt',
120
+ 'ro',
121
+ 'ru',
122
+ 'sd',
123
+ 'si',
124
+ 'sl',
125
+ 'sr',
126
+ 'sv',
127
+ 'ta',
128
+ 'te',
129
+ 'th',
130
+ 'tl',
131
+ 'tr',
132
+ 'ug',
133
+ 'ur',
134
+ 'vi',
135
+ 'zh',
136
+ ]
137
+ end
138
+
139
+ def nullary_operators
140
+ [
141
+ ':(',
142
+ ':)',
143
+ '?',
144
+ 'OR',
145
+ ]
146
+ end
147
+
148
+ def operators
149
+ nullary_operators + unary_operators
150
+ end
151
+
152
+ def possible_operators_for_query(q)
153
+ (nullary_operators.map { |o| "#{o} " } + unary_operators).select { |x| x.start_with?(q) && x != q }
154
+ end
155
+
156
+ def substrs(str)
157
+ 0.upto(str.length - 1).map { |n| str.slice(0...n) }
158
+ end
159
+
160
+ def unary_operators
161
+ [
162
+ '-filter:',
163
+ '-from:',
164
+ '-lang:',
165
+ '-to:',
166
+ '-url:',
167
+ '@',
168
+ 'filter:',
169
+ 'from:',
170
+ 'lang:',
171
+ 'list:',
172
+ 'since:',
173
+ 'to:',
174
+ 'until:',
175
+ 'url:',
176
+ ]
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,37 @@
1
+ require 'twterm/completer/default_completer'
2
+ require 'twterm/completer/screen_name_completer'
3
+ require 'twterm/completer/search_query_completer'
4
+
5
+ module Twterm
6
+ class CompletionManager
7
+ def initialize(app)
8
+ @app = app
9
+
10
+ Readline.completion_case_fold = false
11
+ end
12
+
13
+ def set_default_mode!
14
+ complete_with!(Completer::DefaultCompleter.new(app))
15
+ end
16
+
17
+ def set_screen_name_mode!
18
+ complete_with!(Completer::ScreenNameCompleter.new(app))
19
+ end
20
+
21
+ def set_search_mode!
22
+ complete_with!(Completer::SearchQueryCompleter.new(app))
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :app
28
+
29
+ def complete_with!(completer)
30
+ Readline.basic_word_break_characters = completer.basic_word_break_characters
31
+ Readline.completion_append_character = completer.completion_append_character
32
+ Readline.completion_proc = -> q { completer.complete(q) }
33
+
34
+ self
35
+ end
36
+ end
37
+ end
@@ -74,9 +74,15 @@ module Twterm
74
74
  exit
75
75
  end
76
76
 
77
+ def complete_missing_commands!(dict)
78
+ completed_dict = default_mappings.merge(dict.map { |k, v| [k, v.to_h] }.to_h) { |_, l, r| l.merge(r.to_h) }
79
+
80
+ assign_mappings!(completed_dict)
81
+ save!(completed_dict)
82
+ end
83
+
77
84
  def create_default_dict_file!
78
- dict = TOML.dump(default_mappings).gsub("\n[", "\n\n[")
79
- File.open(dict_file_path, 'w', 0644) { |f| f.write(dict) }
85
+ save!(default_mappings)
80
86
  end
81
87
 
82
88
  def default_mappings
@@ -122,6 +128,12 @@ Press any key to continue
122
128
  getc
123
129
  ensure
124
130
  assign_mappings!(dict || default_mappings)
131
+ complete_missing_commands!(dict) unless dict.nil?
132
+ end
133
+
134
+ def save!(mappings)
135
+ dict = TOML.dump(mappings).gsub("\n[", "\n\n[")
136
+ File.open(dict_file_path, 'w', 0644) { |f| f.write(dict) }
125
137
  end
126
138
  end
127
139
  end
@@ -5,14 +5,17 @@ require 'twterm/key_mapper/no_such_key'
5
5
  module Twterm
6
6
  class KeyMapper
7
7
  class AbstractKeyMapper
8
- def initialize(mappings)
8
+ def initialize(dict)
9
9
  commands = self.class.commands
10
10
 
11
- mappings.keys.each do |k|
11
+ dict ||= {}
12
+
13
+ dict.keys.each do |k|
12
14
  raise NoSuchCommand.new(self.class.category, k) unless commands.include?(k)
13
15
  end
14
16
 
15
- @mappings = Hash[mappings.map { |k, v| [k, translate(v)] }]
17
+ @mappings = Hash[dict.map { |k, v| [k, translate(v)] }]
18
+ @dict = dict
16
19
  end
17
20
 
18
21
  def [](key)
@@ -20,6 +23,10 @@ module Twterm
20
23
  @mappings[key]
21
24
  end
22
25
 
26
+ def to_h
27
+ @dict
28
+ end
29
+
23
30
  private
24
31
 
25
32
  def translate(key)
@@ -7,6 +7,7 @@ class Twterm::KeyMapper::StatusKeyMapper < Twterm::KeyMapper::AbstractKeyMapper
7
7
  destroy: 'D',
8
8
  like: 'F',
9
9
  open_link: 'o',
10
+ quote: 'Q',
10
11
  reply: 'r',
11
12
  retweet: 'R',
12
13
  user: 'U',
@@ -5,6 +5,10 @@ require 'twterm/repository/abstract_repository'
5
5
  module Twterm
6
6
  module Repository
7
7
  class AbstractEntityRepository < AbstractRepository
8
+ def all
9
+ repository.values
10
+ end
11
+
8
12
  def create(*args)
9
13
  invoke_callbacks(:before_create, *args)
10
14
 
@@ -5,11 +5,8 @@ require 'twterm/status'
5
5
  module Twterm
6
6
  module Repository
7
7
  class StatusRepository < AbstractExpirableEntityRepository
8
- def all
9
- repository.values
10
- end
11
-
12
- def create(tweet, is_retweeted_status = false)
8
+ def create(tweet, is_referred_status = false)
9
+ create(tweet.quoted_status, true) unless tweet.quoted_status.is_a?(Twitter::NullObject)
13
10
  create(tweet.retweeted_status, true) unless tweet.retweeted_status.is_a?(Twitter::NullObject)
14
11
  super
15
12
  end
@@ -23,10 +20,15 @@ module Twterm
23
20
  status = super
24
21
 
25
22
  touch(status.retweeted_status_id) if !status.nil? && status.retweet?
23
+ touch(status.quoted_status_id) if !status.nil? && status.quote?
26
24
 
27
25
  status
28
26
  end
29
27
 
28
+ def find_quotes_for(id)
29
+ repository.values.select { |s| s.quoted_status_id == id }
30
+ end
31
+
30
32
  def find_replies_for(id)
31
33
  repository.values.select { |s| s.in_reply_to_status_id == id }
32
34
  end
@@ -5,10 +5,6 @@ require 'twterm/user'
5
5
  module Twterm
6
6
  module Repository
7
7
  class UserRepository < AbstractExpirableEntityRepository
8
- def all
9
- repository.values
10
- end
11
-
12
8
  def ids
13
9
  repository.keys
14
10
  end