twterm 2.0.1 → 2.1.0

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.
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