lightning 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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