clipcellar 0.0.1
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 +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +502 -0
- data/NEWS.md +5 -0
- data/README.md +70 -0
- data/Rakefile +25 -0
- data/bin/clipcellar +21 -0
- data/clipcellar.gemspec +30 -0
- data/lib/clipcellar.rb +23 -0
- data/lib/clipcellar/clipboard.rb +82 -0
- data/lib/clipcellar/command.rb +158 -0
- data/lib/clipcellar/groonga_database.rb +100 -0
- data/lib/clipcellar/groonga_searcher.rb +56 -0
- data/lib/clipcellar/tree_view.rb +108 -0
- data/lib/clipcellar/version.rb +19 -0
- data/lib/clipcellar/window.rb +121 -0
- data/test/run-test.rb +27 -0
- data/test/test-command.rb +53 -0
- metadata +178 -0
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# Clipcellar
|
2
|
+
|
3
|
+
Clipcellar is a full-text searchable data store for clipboard.
|
4
|
+
|
5
|
+
Powered by [GTK+][] (via [Ruby/GTK2][]) and [Groonga][] (via [Rroonga][]).
|
6
|
+
|
7
|
+
[GTK+]:http://www.gtk.org/
|
8
|
+
[Ruby/GTK2]:http://ruby-gnome2.sourceforge.jp/
|
9
|
+
[Groonga]:http://groonga.org/
|
10
|
+
[Rroonga]:http://ranguba.org/
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
gem 'clipcellar'
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install clipcellar
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
### Show help
|
29
|
+
|
30
|
+
$ clipcellar
|
31
|
+
|
32
|
+
### Add a text (or clipboard) to data store
|
33
|
+
|
34
|
+
$ clipcellar set [TEXT]
|
35
|
+
|
36
|
+
### Add a text from files (or stdin)
|
37
|
+
|
38
|
+
$ clipcellar input [FILE]...
|
39
|
+
|
40
|
+
### Show added texts in data store
|
41
|
+
|
42
|
+
$ clipcellar show [--gui]
|
43
|
+
|
44
|
+
### Full-text search (and set a text to clipboard)
|
45
|
+
|
46
|
+
$ clipcellar search WORD... [--gui]
|
47
|
+
|
48
|
+
### Watch clipboard (Ctrl+C to stop)
|
49
|
+
|
50
|
+
$ clipcellar watch
|
51
|
+
|
52
|
+
### Delete data store and all added texts
|
53
|
+
|
54
|
+
$ clipcellar destroy
|
55
|
+
|
56
|
+
## License
|
57
|
+
|
58
|
+
Copyright (c) 2014 Masafumi Yokoyama `<myokoym@gmail.com>`
|
59
|
+
|
60
|
+
LGPLv2.1 or later.
|
61
|
+
|
62
|
+
See LICENSE.txt or http://www.gnu.org/licenses/lgpl-2.1 for details.
|
63
|
+
|
64
|
+
## Contributing
|
65
|
+
|
66
|
+
1. Fork it
|
67
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
68
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
69
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
70
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require "bundler/gem_tasks"
|
18
|
+
|
19
|
+
desc "Run test"
|
20
|
+
task :test do
|
21
|
+
Bundler::GemHelper.install_tasks
|
22
|
+
ruby("test/run-test.rb")
|
23
|
+
end
|
24
|
+
|
25
|
+
task :default => :test
|
data/bin/clipcellar
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation; either
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
|
+
|
19
|
+
require "clipcellar/command"
|
20
|
+
|
21
|
+
Clipcellar::Command.start
|
data/clipcellar.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'clipcellar/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "clipcellar"
|
8
|
+
spec.version = Clipcellar::VERSION
|
9
|
+
spec.authors = ["Masafumi Yokoyama"]
|
10
|
+
spec.email = ["myokoym@gmail.com"]
|
11
|
+
spec.summary = %q{Full-Text Searchable Data Store for Clipboard}
|
12
|
+
spec.description = %q{Clipcellar is a full-text searchable data store for clipboard by GTK+ (via Ruby/GTK2) and Groonga (via Rroonga).}
|
13
|
+
spec.homepage = "https://github.com/myokoym/clipcellar"
|
14
|
+
spec.license = "LGPLv2.1 or later"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) {|f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency("gtk2")
|
22
|
+
spec.add_runtime_dependency("rroonga")
|
23
|
+
spec.add_runtime_dependency("thor")
|
24
|
+
|
25
|
+
spec.add_development_dependency("test-unit")
|
26
|
+
spec.add_development_dependency("test-unit-notify")
|
27
|
+
spec.add_development_dependency("test-unit-rr")
|
28
|
+
spec.add_development_dependency("bundler", "~> 1.6")
|
29
|
+
spec.add_development_dependency("rake")
|
30
|
+
end
|
data/lib/clipcellar.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require "clipcellar/version"
|
18
|
+
require "clipcellar/groonga_database"
|
19
|
+
require "clipcellar/groonga_searcher"
|
20
|
+
require "clipcellar/clipboard"
|
21
|
+
|
22
|
+
module Clipcellar
|
23
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
begin
|
18
|
+
require "gtk2"
|
19
|
+
rescue Gtk::InitError
|
20
|
+
# a window system is unnecessary.
|
21
|
+
end
|
22
|
+
|
23
|
+
module Clipcellar
|
24
|
+
class Clipboard
|
25
|
+
class << self
|
26
|
+
def watch
|
27
|
+
current_text = get
|
28
|
+
GLib::Timeout.add(500) do
|
29
|
+
text = get
|
30
|
+
if current_text != text
|
31
|
+
yield(text)
|
32
|
+
current_text = text
|
33
|
+
end
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
begin
|
38
|
+
Gtk.main
|
39
|
+
rescue Interrupt => e
|
40
|
+
$stderr.puts(e.message)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def get
|
45
|
+
if /darwin/ =~ RUBY_PLATFORM
|
46
|
+
`pbpaste`
|
47
|
+
else
|
48
|
+
clipboard.wait_for_text
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def set(text)
|
53
|
+
copy_to_clipboard(text)
|
54
|
+
|
55
|
+
# workaround for CLI
|
56
|
+
GLib::Timeout.add(1) do
|
57
|
+
Gtk.main_quit
|
58
|
+
end
|
59
|
+
Gtk.main
|
60
|
+
end
|
61
|
+
|
62
|
+
def copy_to_clipboard(text)
|
63
|
+
if /darwin/ =~ RUBY_PLATFORM
|
64
|
+
require "tempfile"
|
65
|
+
Tempfile.open(["clipcellar", "w"]) do |file|
|
66
|
+
text.each_line do |line|
|
67
|
+
file.puts(line)
|
68
|
+
end
|
69
|
+
file.flush
|
70
|
+
system("pbcopy < #{file.path}")
|
71
|
+
end
|
72
|
+
else
|
73
|
+
clipboard.text = text
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def clipboard
|
78
|
+
Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require "thor"
|
18
|
+
require "fileutils"
|
19
|
+
require "clipcellar/version"
|
20
|
+
require "clipcellar/groonga_database"
|
21
|
+
require "clipcellar/groonga_searcher"
|
22
|
+
require "clipcellar/clipboard"
|
23
|
+
require "clipcellar/window"
|
24
|
+
|
25
|
+
module Clipcellar
|
26
|
+
class Command < Thor
|
27
|
+
map "-v" => :version
|
28
|
+
map "-a" => :set
|
29
|
+
map "-A" => :input
|
30
|
+
map "-s" => :show
|
31
|
+
map "-S" => :search
|
32
|
+
map "-W" => :watch
|
33
|
+
|
34
|
+
attr_reader :database_dir
|
35
|
+
|
36
|
+
def initialize(*args)
|
37
|
+
super
|
38
|
+
@base_dir = File.join(File.expand_path("~"), ".clipcellar")
|
39
|
+
@database_dir = File.join(@base_dir, "db")
|
40
|
+
end
|
41
|
+
|
42
|
+
desc "version", "Show version number."
|
43
|
+
def version
|
44
|
+
puts Clipcellar::VERSION
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "set [TEXT]", "Add a text (or clipboard) to data store"
|
48
|
+
def set(text=nil)
|
49
|
+
text = Clipboard.get unless text
|
50
|
+
return unless text
|
51
|
+
Clipboard.set(text)
|
52
|
+
add(text)
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "input [FILE]...", "Add a text from files (or stdin)"
|
56
|
+
def input(*files)
|
57
|
+
ARGV.shift
|
58
|
+
text = ""
|
59
|
+
while line = ARGF.gets
|
60
|
+
text << line
|
61
|
+
end
|
62
|
+
Clipboard.set(text)
|
63
|
+
add(text)
|
64
|
+
end
|
65
|
+
|
66
|
+
desc "watch", "Watch clipboard to add clipboard to data store"
|
67
|
+
def watch
|
68
|
+
$stderr.puts("Watching... (Ctrl+C to Stop)")
|
69
|
+
Clipboard.watch do |text|
|
70
|
+
add(text)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
desc "show [--gui]", "Show added texts in data store"
|
75
|
+
option :gui, :type => :boolean, :desc => "GUI mode"
|
76
|
+
def show
|
77
|
+
records = []
|
78
|
+
GroongaDatabase.new.open(@database_dir) do |database|
|
79
|
+
database.clipboards.each do |clipboard|
|
80
|
+
record = {}
|
81
|
+
record[:key] = clipboard._key
|
82
|
+
record[:text] = clipboard.text
|
83
|
+
record[:time] = clipboard.created_at
|
84
|
+
records << record
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
records.sort_by! do |record|
|
89
|
+
record[:time]
|
90
|
+
end
|
91
|
+
|
92
|
+
if options[:gui]
|
93
|
+
records.reverse!
|
94
|
+
window = Window.new(records)
|
95
|
+
window.run
|
96
|
+
else
|
97
|
+
records.each do |record|
|
98
|
+
text = record[:text]
|
99
|
+
time = record[:time].strftime("%Y-%m-%d %H:%M:%S")
|
100
|
+
formatted_text = text.gsub(/\n/, " ") if text
|
101
|
+
puts "#{time} #{formatted_text}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
desc "search WORD... [--gui]", "Search texts from data store"
|
107
|
+
option :gui, :type => :boolean, :desc => "GUI mode"
|
108
|
+
def search(required_word, *optional_words)
|
109
|
+
words = [required_word]
|
110
|
+
words += optional_words
|
111
|
+
|
112
|
+
records = []
|
113
|
+
GroongaDatabase.new.open(@database_dir) do |database|
|
114
|
+
reverse = options[:gui] ? true : false
|
115
|
+
sorted_clipboards = GroongaSearcher.search(database,
|
116
|
+
words,
|
117
|
+
:reverse => reverse)
|
118
|
+
|
119
|
+
sorted_clipboards.each do |clipboard|
|
120
|
+
record = {}
|
121
|
+
record[:key] = clipboard._key
|
122
|
+
record[:text] = clipboard.text
|
123
|
+
record[:time] = clipboard.created_at
|
124
|
+
records << record
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
if options[:gui]
|
129
|
+
window = Window.new(records)
|
130
|
+
window.run
|
131
|
+
else
|
132
|
+
text = nil
|
133
|
+
records.each do |record|
|
134
|
+
text = record[:text]
|
135
|
+
time = record[:time].strftime("%Y-%m-%d %H:%M:%S")
|
136
|
+
formatted_text = text.gsub(/\n/, " ") if text
|
137
|
+
puts "#{time} #{formatted_text}"
|
138
|
+
end
|
139
|
+
Clipboard.set(text) if text
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
desc "destroy", "Delete data store and all added texts"
|
144
|
+
def destroy
|
145
|
+
FileUtils.rm(Dir.glob(File.join(@database_dir, "clipcellar.db*")))
|
146
|
+
FileUtils.rmdir(@database_dir)
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
def add(text)
|
151
|
+
time = Time.now
|
152
|
+
id = time.strftime("%Y%m%d%H%M%S%L")
|
153
|
+
GroongaDatabase.new.open(@database_dir) do |database|
|
154
|
+
database.add(id, text, time)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require "groonga"
|
18
|
+
|
19
|
+
module Clipcellar
|
20
|
+
class GroongaDatabase
|
21
|
+
def initialize
|
22
|
+
@database = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def open(base_path, encoding=:utf8)
|
26
|
+
reset_context(encoding)
|
27
|
+
path = File.join(base_path, "clipcellar.db")
|
28
|
+
if File.exist?(path)
|
29
|
+
@database = Groonga::Database.open(path)
|
30
|
+
populate_schema
|
31
|
+
else
|
32
|
+
FileUtils.mkdir_p(base_path)
|
33
|
+
populate(path)
|
34
|
+
end
|
35
|
+
if block_given?
|
36
|
+
begin
|
37
|
+
yield(self)
|
38
|
+
ensure
|
39
|
+
close unless closed?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def add(id, text, time)
|
45
|
+
clipboards.add(id,
|
46
|
+
{
|
47
|
+
:text => text,
|
48
|
+
:created_at => time,
|
49
|
+
})
|
50
|
+
end
|
51
|
+
|
52
|
+
def delete(id)
|
53
|
+
clipboards.delete(id)
|
54
|
+
end
|
55
|
+
|
56
|
+
def close
|
57
|
+
@database.close
|
58
|
+
@database = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def closed?
|
62
|
+
@database.nil? or @database.closed?
|
63
|
+
end
|
64
|
+
|
65
|
+
def clipboards
|
66
|
+
@clipboards ||= Groonga["Clipboards"]
|
67
|
+
end
|
68
|
+
|
69
|
+
def dump
|
70
|
+
Groonga::DatabaseDumper.dump
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
def reset_context(encoding)
|
75
|
+
Groonga::Context.default_options = {:encoding => encoding}
|
76
|
+
Groonga::Context.default = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def populate(path)
|
80
|
+
@database = Groonga::Database.create(:path => path)
|
81
|
+
populate_schema
|
82
|
+
end
|
83
|
+
|
84
|
+
def populate_schema
|
85
|
+
Groonga::Schema.define do |schema|
|
86
|
+
schema.create_table("Clipboards", :type => :hash) do |table|
|
87
|
+
table.text("text")
|
88
|
+
table.time("created_at")
|
89
|
+
end
|
90
|
+
|
91
|
+
schema.create_table("Terms",
|
92
|
+
:type => :patricia_trie,
|
93
|
+
:key_normalize => true,
|
94
|
+
:default_tokenizer => "TokenBigram") do |table|
|
95
|
+
table.index("Clipboards.text")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|