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 +4 -4
- data/lib/twterm.rb +1 -1
- data/lib/twterm/app.rb +2 -2
- data/lib/twterm/completer/abstract_completer.rb +25 -0
- data/lib/twterm/completer/default_completer.rb +21 -0
- data/lib/twterm/completer/screen_name_completer.rb +17 -0
- data/lib/twterm/completer/search_query_completer.rb +180 -0
- data/lib/twterm/completion_manager.rb +37 -0
- data/lib/twterm/key_mapper.rb +14 -2
- data/lib/twterm/key_mapper/abstract_key_mapper.rb +10 -3
- data/lib/twterm/key_mapper/status_key_mapper.rb +1 -0
- data/lib/twterm/repository/abstract_entity_repository.rb +4 -0
- data/lib/twterm/repository/status_repository.rb +7 -5
- data/lib/twterm/repository/user_repository.rb +0 -4
- data/lib/twterm/rest_client.rb +7 -9
- data/lib/twterm/scheduler.rb +1 -0
- data/lib/twterm/status.rb +16 -1
- data/lib/twterm/tab/new/search.rb +2 -2
- data/lib/twterm/tab/new/user.rb +1 -1
- data/lib/twterm/tab/statuses/base.rb +9 -1
- data/lib/twterm/tab/statuses/cacheable.rb +18 -0
- data/lib/twterm/tab/statuses/conversation.rb +66 -1
- data/lib/twterm/tab/statuses/list_timeline.rb +2 -2
- data/lib/twterm/tab/statuses/user_timeline.rb +11 -2
- data/lib/twterm/tweetbox.rb +46 -57
- data/lib/twterm/version.rb +1 -1
- data/spec/fixtures/list.json +69 -0
- data/spec/twterm/completer/search_query_completer_spec.rb +231 -0
- data/twterm.gemspec +1 -1
- metadata +15 -8
- data/lib/twterm/completion_mamanger.rb +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a822edd0e5719ca957ab183b1859bd6ec01113ca
|
4
|
+
data.tar.gz: ecec2ab95f599a575226d0a3239f1b93ef63a4f9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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/
|
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/
|
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
|
-
@
|
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
|
data/lib/twterm/key_mapper.rb
CHANGED
@@ -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
|
-
|
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(
|
8
|
+
def initialize(dict)
|
9
9
|
commands = self.class.commands
|
10
10
|
|
11
|
-
|
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[
|
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)
|
@@ -5,11 +5,8 @@ require 'twterm/status'
|
|
5
5
|
module Twterm
|
6
6
|
module Repository
|
7
7
|
class StatusRepository < AbstractExpirableEntityRepository
|
8
|
-
def
|
9
|
-
|
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
|