lightning 0.2.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.
@@ -0,0 +1,13 @@
1
+ == 0.2.1
2
+ * Rubyforge release
3
+ * Removed monkey patches
4
+
5
+ == 0.2.0
6
+ * Added regex completion
7
+ * Added global and local aliases
8
+
9
+ == 0.1.2
10
+ * Major refactoring
11
+
12
+ == 0.1.1
13
+ * Initial release
@@ -0,0 +1,22 @@
1
+ The MIT LICENSE
2
+
3
+ Copyright (c) 2009 Gabriel Horner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,150 @@
1
+ == Description
2
+
3
+ Autocomplete a group of files and directories simply by listing their globbable paths
4
+ in a config file. Generate completions from your config, source them into your shell
5
+ and you're ready to rock.
6
+
7
+ So instead of carpal-typing
8
+
9
+ bash> vim /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/irb.rb
10
+
11
+ you type or complete
12
+
13
+ bash> rvim irb.rb
14
+
15
+ Uneasy about what lightning will execute? Test/print it out with a -test flag
16
+
17
+ bash> rvim -test irb.rb
18
+ rvim '/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/irb.rb'
19
+
20
+ Want to autocomplete but don't remember how the basename starts? Just use a ruby regular expression:
21
+
22
+ # *'s are converted to .*'s for convience sakes
23
+ bash> rvim *dialog [TAB TAB]
24
+ canvasprintdialog.rb
25
+ extfileselectiondialog.rb
26
+ dialog.rb//System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/tk
27
+ fileselectiondialog.rb
28
+ dialog.rb//System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/tkextlib/bwidget
29
+ finddialog.rb
30
+
31
+ #re-edit your line to narrow down your completion to one entry
32
+ bash> rvim ca*dialog [TAB TAB]
33
+
34
+ #once the basename completes, you can execute your command
35
+ bash> rvim canvasprintdialog.rb
36
+
37
+ As you can see, you only need to autocomplete the basenames of paths and lightning will resolve their
38
+ full paths. In these examples, rvim is a lightning command configured to autocomplete a certain group of paths for vim.
39
+ In my case, rvim is configured to complete my ruby core and standard library files.
40
+
41
+
42
+ == Install
43
+
44
+ For newcomers to github, install this gem with: `gem install lightning`
45
+
46
+ To make your own commands, you'll need to:
47
+
48
+ 1. Create ~/.lightning.yml or a lightning.yml in the current directory.
49
+ Use the Configuration section below and the provided lightning.yml.example as guides.
50
+
51
+ 2. Execute `lightning-install` to generate ~/.lightning_completions from your config.
52
+ There is a config option for changing the location of the generated file. See Configuration
53
+ below. See lightning_completions.example for what would be generated for the enclosed example
54
+ config.
55
+
56
+ 3. Source the generated file in your bashrc ie `source ~/.lightning_completions`.
57
+
58
+
59
+ == Globbable Paths
60
+
61
+ Since the globbable paths are interpreted by ruby's Dir.glob(), you can:
62
+
63
+ 1. Autocomplete files and directories that don't start with specific letters.
64
+
65
+ Dir.glob("[^ls]*") -> Matches anything not starting with l or s
66
+
67
+ 2. Autocomplete files ending with specific file extensions for a given directory.
68
+
69
+ Dir.glob("/painfully/long/path/*.{rb,erb}") -> Matches files ending with .rb or .erb
70
+
71
+ 3. Autocomplete all directories however many levels deep under the current directory.
72
+
73
+ Dir.glob("**/")
74
+
75
+ `ri Dir.glob` for more examples.
76
+
77
+ == Aliases
78
+
79
+ Lightning supports custom aliases for any path, globally and per command. So if there is some
80
+ path that you access often but that's still too slow with completion, alias it away!
81
+
82
+ == Configuration
83
+
84
+ It helps to look at lightning.yml.example while reading this.
85
+
86
+ Configuration options are:
87
+
88
+ * generated_file: Location of shell script file generated from config. Defaults to
89
+ ~/.lightning_completions.
90
+ * ignore_paths: List of paths to globally ignore when listing completions.
91
+ * complete_regex: true or false (default is true)
92
+ Turns on/off Ruby regular expression matching when completing. One convience
93
+ is that a '*' is converted to '.*' ie glob-like behavior.
94
+
95
+ Note: Realize your regular expression normally just match the basename. However, since duplicates
96
+ list their full paths, their full paths are subject to regex matching.
97
+ * shell: Specifies shell script generator used for generating completions. Defaults to bash.
98
+ * aliases: A hash (pairs) of custom aliases pointing to full paths. These aliases will be globally
99
+ recognized by any lightning command.
100
+ * commands: A list of lightning commands. A lightning command is just a shell function
101
+ which executes a specified shell function with a defined set of paths to autocomplete on.
102
+ A command consists of the following options/keys:
103
+
104
+ * name (required): Name of the lightning command you'll be typing.
105
+ * map_to (required): Shell command which the lightning command passes arguments to.
106
+ * description: Description which is placed as a comment in the generated shell script.
107
+ * paths (required): A list of globbable paths, whose basenames are autocompleted. You can also
108
+ pass this a path name that has been defined in the paths option.
109
+ * aliases: A hash (pairs) of custom aliases and full paths only for this command.
110
+
111
+ * paths: This takes a hash (pairs) of path names and globbable paths. This should be used when
112
+ you have a group of paths that are used in multiple commands. When doing that, you would specify
113
+ the path name defined here in the command's paths key.
114
+ Note: path names should only be alphanumeric
115
+
116
+ * post_path: Text to add immediately after a resolved path. lightning.yml.example contains
117
+ an example used for opening gem documentation in your browser.
118
+
119
+ == Duplicate Basenames
120
+
121
+ So what happens when their are multiple matches for the same basename?
122
+ Lightning appends a '/' + the full directory to each of the basenames.
123
+
124
+ For example, if I autocomplete button.rb for my ruby standard libraries I get:
125
+
126
+ bash> rvim button.rb
127
+ button.rb//System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/tk
128
+ button.rb//System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/tkextlib/bwidget
129
+ button.rb//System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/tkextlib/blt/tile
130
+
131
+ This isn't the prettiest completion but it resolves duplicates, displays the path differences
132
+ between each and easily autocompletes. I'm open to suggestions on this syntax.
133
+
134
+ == Limitations
135
+ * The generated shell scripts default to bash but could easily be extended for other shell languages. Patches welcome.
136
+ * All arguments passed to a lightning command are considered part of the basename to resolve. So
137
+ it's not yet possible to resolve some arguments and not others.
138
+
139
+ == Motivation
140
+
141
+ I've seen dotfiles on github and on blogs which accomplish this kind of autocompletion for gem
142
+ documentation. There's even a gem just for gem editing: http://gemedit.rubyforge.org/.
143
+ But once I saw how easy it was to manipulate completion through ruby,
144
+ http://github.com/ryanb/dotfiles/blob/master/bash/completion_scripts/project_completion,
145
+ I had to do something.
146
+
147
+ == Todo
148
+ * Binary to interact with common config operations including add/remove current directory or globs from a command
149
+ * Allow lightning commands to only path-resolve one of multiple arguments given.
150
+ * An irb generator
@@ -0,0 +1,69 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ begin
5
+ require 'rcov/rcovtask'
6
+
7
+ Rcov::RcovTask.new do |t|
8
+ t.libs << 'test'
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ t.rcov_opts = ["-T -x '/Library/Ruby/*'"]
11
+ t.verbose = true
12
+ end
13
+ rescue LoadError
14
+ puts "Rcov not available. Install it for rcov-related tasks with: sudo gem install rcov"
15
+ end
16
+
17
+ begin
18
+ require 'jeweler'
19
+ Jeweler::Tasks.new do |s|
20
+ s.name = "lightning"
21
+ s.executables = ["lightning-complete", "lightning-full_path", "lightning-install"]
22
+ s.summary = "Path completions for your shell that will let you navigate like lightning."
23
+ s.description = "Lightning creates shell commands that each autocomplete to a configured group of files and directories. Autocompleting is quick since you only need to type the basename and can even use regex completion."
24
+ s.email = "gabriel.horner@gmail.com"
25
+ s.homepage = "http://github.com/cldwalker/lightning"
26
+ s.authors = ["Gabriel Horner"]
27
+ s.files = FileList["CHANGELOG.rdoc", "Rakefile", "VERSION.yml", "lightning_completions.example", "lightning.yml.example","README.rdoc", "LICENSE.txt", "{bin,lib,test}/**/*"]
28
+ s.has_rdoc = true
29
+ s.rubyforge_project = 'tagaholic'
30
+ s.extra_rdoc_files = ["README.rdoc", "LICENSE.txt"]
31
+ end
32
+
33
+ rescue LoadError
34
+ puts "Jeweler not available. Install it for jeweler-related tasks with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
35
+ end
36
+
37
+ Rake::TestTask.new do |t|
38
+ t.libs << 'lib'
39
+ t.pattern = 'test/**/*_test.rb'
40
+ t.verbose = false
41
+ end
42
+
43
+ Rake::RDocTask.new do |rdoc|
44
+ rdoc.rdoc_dir = 'rdoc'
45
+ rdoc.title = 'test'
46
+ rdoc.options << '--line-numbers' << '--inline-source'
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
50
+
51
+ namespace :dev do
52
+ desc "Generates completion, modifies it for local development"
53
+ task :reload=>:generate_completions do
54
+ file = 'lightning_completions'
55
+ string = File.read(file)
56
+ string.sub!(/^LBIN_PATH/,'#LBIN_PATH')
57
+ string.sub!(/^#LBIN_PATH/,'LBIN_PATH')
58
+ File.open(file,'w') {|f| f.write(string) }
59
+ end
60
+
61
+ desc "Generates local completion file to be sourced by your shell"
62
+ task :generate_completions do
63
+ $:.unshift 'lib'
64
+ require 'lightning'
65
+ Lightning::Generator.generate_completions 'lightning_completions'
66
+ end
67
+ end
68
+
69
+ task :default => :test
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 2
3
+ :patch: 1
4
+ :major: 0
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # == Description
4
+ # Used to complete a lightning command
5
+ require File.expand_path(File.join(File.dirname(__FILE__), "..","lib","lightning"))
6
+
7
+ if (command = ARGV.shift)
8
+ puts Lightning.complete(command, ENV["COMP_LINE"])
9
+ exit 0
10
+ else
11
+ puts "No command given"
12
+ exit 1
13
+ end
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # == Description
4
+ # Used by path completion functions to return first possible full path which matches the given basename for the given command.
5
+
6
+ require File.expand_path(File.join(File.dirname(__FILE__), "..","lib","lightning"))
7
+
8
+ command = ARGV.shift
9
+ if command.nil?
10
+ puts "No command given"
11
+ exit 1
12
+ end
13
+ if ARGV.empty?
14
+ puts "No arguments given"
15
+ exit 1
16
+ end
17
+ puts Lightning.translate(command, ARGV)
18
+ exit 0
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # == Description
4
+ # Used to generate shell file from configuration
5
+
6
+ require File.expand_path(File.join(File.dirname(__FILE__), "..","lib","lightning"))
7
+ Lightning::Generator.generate_completions ARGV.shift
@@ -0,0 +1,68 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__))
2
+ require 'yaml'
3
+ require 'lightning/bolt'
4
+ require 'lightning/bolts'
5
+ require 'lightning/completion'
6
+ require 'lightning/config'
7
+ require 'lightning/completion_map'
8
+ require 'lightning/generator'
9
+
10
+ class Lightning
11
+ TEST_FLAG = '-test'
12
+ class<<self
13
+ attr_accessor :current_command
14
+ def config(options={})
15
+ @config ||= Config.create(options)
16
+ end
17
+
18
+ def load_read_only_config
19
+ @config = Config.create(:read_only=>true)
20
+ end
21
+
22
+ def config=(value)
23
+ @config = Config.new(value)
24
+ end
25
+
26
+ def complete(command, text_to_complete)
27
+ load_read_only_config
28
+ @current_command = command
29
+ if bolt_key = config_command(command)['bolt_key']
30
+ Completion.complete(text_to_complete, bolt_key)
31
+ else
32
+ ["#Error: No paths found for this command.", "If this is a bug contact me."]
33
+ end
34
+ end
35
+
36
+ def translate(command, argv)
37
+ load_read_only_config
38
+ @current_command = command
39
+ if bolt_key = config_command(command)['bolt_key']
40
+ bolts[bolt_key].resolve_completion(argv)
41
+ else
42
+ '#Error-no_paths_found_for_this_command'
43
+ end
44
+ end
45
+
46
+ def bolts
47
+ @bolts ||= Bolts.new
48
+ end
49
+
50
+ def config_command(name, hardcheck=false)
51
+ command = config[:commands].find {|e| e['name'] == name} || {}
52
+ if hardcheck && command.empty?
53
+ puts "Command '#{name}' not found"
54
+ nil
55
+ else
56
+ command
57
+ end
58
+ end
59
+
60
+ def ignore_paths
61
+ unless @ignore_paths
62
+ @ignore_paths = []
63
+ @ignore_paths += config[:ignore_paths] if config[:ignore_paths] && !config[:ignore_paths].empty?
64
+ end
65
+ @ignore_paths
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,33 @@
1
+ # A bolt, referenced by a key, is the basic unit needed to access a lightning command's functionality.
2
+ class Lightning
3
+ class Bolt
4
+ attr_reader :key
5
+ def initialize(bolt_key)
6
+ @key = bolt_key
7
+ end
8
+
9
+ def completions
10
+ completion_map.keys
11
+ end
12
+
13
+ def completion_map
14
+ @completion_map ||= Lightning::CompletionMap.new(self.paths,
15
+ :global_aliases=>Lightning.config[:aliases],
16
+ :aliases=>Lightning.config_command(Lightning.current_command)['aliases'])
17
+ end
18
+
19
+ def resolve_completion(basename)
20
+ basename = basename.join(" ") if basename.is_a?(Array)
21
+ basename.gsub!(/\s*#{TEST_FLAG}\s*/,'')
22
+ #TODO
23
+ # if (regex = Lightning.config_command(Lightning.current_command)['completion_regex'])
24
+ # basename = basename[/#{regex}/]
25
+ # end
26
+ completion_map[basename] || ''
27
+ end
28
+
29
+ def paths
30
+ @paths ||= Lightning.config[:paths][key] || []
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,12 @@
1
+ # Hash of bolts accessed by their keys
2
+ class Lightning
3
+ class Bolts
4
+ def initialize
5
+ @hash = {}
6
+ end
7
+
8
+ def [](key)
9
+ @hash[key] ||= Lightning::Bolt.new(key)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,92 @@
1
+ class Lightning
2
+ # TODO: Provide commands for the lightning binary.
3
+ module Commands
4
+ # @options :search=>:string, :command=>:string
5
+ def lightning_command(*args)
6
+ puts "Lightning Commands"
7
+ if options[:command]
8
+ lightning_commands = Lightning.config[:commands].select {|e| e['map_to'] == options['command']}
9
+ else
10
+ lightning_commands = Lightning.config[:commands]
11
+ end
12
+ puts lightning_commands.map {|e| e['name'] }.join("\n")
13
+ end
14
+
15
+ #@options :add=>:boolean, :delete=>:boolean
16
+ def path(*args)
17
+ command_name = args.shift
18
+ if options['delete']
19
+ return puts("Path needed") if args[0].nil?
20
+ delete_path(command_name, args[0])
21
+ elsif options['add']
22
+ return puts("Path needed") if args[0].nil?
23
+ add_path(command_name, args[0])
24
+ else
25
+ puts "Paths for command '#{command_name}'"
26
+ puts config_command_paths(command_name).join("\n")
27
+ end
28
+ end
29
+
30
+ #@options :delete=>:boolean, :add=>:boolean, :global=>:boolean
31
+ def alias(*args)
32
+ if options['delete']
33
+ delete_command_alias(*args.slice(0,2))
34
+ elsif options['add']
35
+ add_command_alias(*args.slice(0,3))
36
+ else
37
+ puts "Aliases"
38
+ (Lightning.config[:aliases] || []).each do |path_alias, path|
39
+ puts "#{path_alias} : #{path}"
40
+ end
41
+ end
42
+ end
43
+
44
+ private
45
+ def config_command_paths(name)
46
+ if command = Lightning.config_command(name, true)
47
+ if command['paths'].is_a?(String)
48
+ Lightning.config[:paths][command['paths']]
49
+ else
50
+ command['paths']
51
+ end
52
+ else
53
+ []
54
+ end
55
+ end
56
+
57
+ def add_command_alias(command_name, path_alias, path)
58
+ if (command = Lightning.config_command(command_name, true))
59
+ path = File.expand_path(path)
60
+ command['aliases'][path_alias] = path
61
+ Lightning.config.save
62
+ puts "Path alias '#{path_alias}' added"
63
+ end
64
+ end
65
+
66
+ def delete_command_alias(command_name, path_alias)
67
+ if (command = Lightning.config_command(command_name, true))
68
+ command['aliases'].delete(path_alias)
69
+ Lightning.config.save
70
+ puts "Path alias '#{path_alias}' deleted"
71
+ end
72
+ end
73
+
74
+ def add_path(command_name, path)
75
+ if (command = Lightning.config_command(command_name, true))
76
+ path = File.expand_path(path)
77
+ config_command_paths(command_name) << path
78
+ Lightning.config.save
79
+ puts "Path '#{path}' added"
80
+ end
81
+ end
82
+
83
+ def delete_path(command_name, path)
84
+ if (command = Lightning.config_command(command_name, true))
85
+ path = File.expand_path(path)
86
+ config_command_paths(command_name).delete(path)
87
+ Lightning.config.save
88
+ puts "Path '#{path}' deleted"
89
+ end
90
+ end
91
+ end
92
+ end