3 # First assume we are installed as a gem
7 # Failing that, try to load our files from the source tree
8 $: << "#{File.dirname(__FILE__)}/../lib"
12 class OhcountCommandLine
15 def initialize(args=[])
16 args = args.clone # Because shift is destructive
17 set_option(args.shift) while args.first =~ /^-/
27 @files = nil # If we change the search paths, clear the cache filenames
31 return @files if @files
33 @files = `find #{(self.paths.any? ? self.paths : ['.']).collect{ |s| "'#{s}'" }.join(' ')} \! -path "*/.*" -type f -print`.split("\n")
34 exit 1 unless @files.any?
40 sfc = Ohcount::SimpleFileContext.new(file, files)
41 polyglot = Ohcount::Detector.detect(sfc)
43 Ohcount::parse(sfc.contents, polyglot) do |language, semantic, line|
44 puts "#{language}\t#{semantic}\t#{line}"
50 # Find all source code files
53 sfc = Ohcount::SimpleFileContext.new(file, files)
54 polyglot = Ohcount::Detector.detect(sfc)
55 puts "#{polyglot}\t#{file}" if polyglot
61 Usage: ohcount [option] [paths...]
63 Ohloh source code line counter command line tool.
66 [option] can be one of the following:
72 -a, --annotate Show annotated source code
74 The contents of all source code files found within the given
75 paths will be emitted to stdout. Each line will be prefixed with
76 a tab-delimited language name and semantic categorization (code,
79 -d, --detect Find source code files
81 Recursively find all source code files within the given paths.
82 For each source code file found, the file name will be emitted to
83 stdout prefixed with a tab-delimited language name.
85 -h, --help Display this message
87 -i, --individual Count lines of code per file
89 Count lines in all source code files within the given paths, and
90 emit a report of the lines of code, comments, and blanks in each
93 -s, --summary Count lines of code (default)
95 Count lines in all source code files within the given paths, and
96 emit a report of the total number of lines of code, comments,
97 and blanks in each language. This is the default action.
99 [paths] can refer to any number of individual files or directories.
100 Directories will be probed recursively. If no path is given,
101 the current directory will be used.
107 STDOUT.write "Examining #{files.size} file(s)"
110 puts "Ohloh Line Count".center(76)
111 puts "File Language Code Comment Comment % Blank Total"
112 puts "----------------------------------------------- -------------- --------- --------- --------- --------- ---------"
117 sfc = Ohcount::SimpleFileContext.new(file, files)
118 polyglot = Ohcount::Detector.detect(sfc)
120 Ohcount::parse(sfc.contents, polyglot) do |language_name, semantic, line|
121 counts[language_name] ||={:code => 0, :comment => 0, :blank => 0}
122 counts[language_name][semantic] += 1
123 languages_found << language_name unless languages_found.include?(language_name)
127 counts.keys.sort{ |a,b| counts[b][:code] <=> counts[a][:code] }.each do |key|
128 write_individual_row(file, key, counts[key][:code],counts[key][:comment],counts[key][:blank])
133 def write_individual_row(file, name, code, comment, blank)
134 printf("%-48s", file)
135 printf(" %-14s",name)
137 printf(" %10d",comment)
139 printf(" %9.1f%%", comment.to_f / (comment+code).to_f * 100.0)
143 printf(" %10d",blank)
144 printf(" %10d\n",code+comment+blank)
149 STDOUT.write "Examining #{files.size} file(s)"
152 any_language_file_count = 0
157 sfc = Ohcount::SimpleFileContext.new(file, files)
158 polyglot = Ohcount::Detector.detect(sfc)
160 Ohcount::parse(sfc.contents, polyglot) do |language_name, semantic, line|
161 counts[language_name] ||= {:code => 0, :comment => 0, :blank => 0}
162 counts[language_name][semantic] += 1
163 languages_found << language_name unless languages_found.include?(language_name)
167 # Keep a running total of the number of files that include a specific language
168 languages_found.each { |l| counts[l][:files] = (counts[l][:files] || 0) + 1 }
170 # Keep a running total of the number of files that include any language
171 any_language_file_count += 1 if languages_found.any?
174 if (progress % 100 == 0)
181 puts "Ohloh Line Count Summary".center(76)
184 puts "Language Files Code Comment Comment % Blank Total"
185 puts "-------------- ----- --------- --------- --------- --------- ---------"
187 counts.keys.sort{ |a,b| counts[b][:code] <=> counts[a][:code] }.each do |key|
188 write_summary_row(key, counts[key][:files], counts[key][:code], counts[key][:comment], counts[key][:blank])
191 puts "-------------- ----- --------- --------- --------- --------- ---------"
192 write_summary_row('Total', any_language_file_count,
193 counts.values.inject(0) { |sum, v| sum + v[:code] },
194 counts.values.inject(0) { |sum, v| sum + v[:comment] },
195 counts.values.inject(0) { |sum, v| sum + v[:blank] })
198 def write_summary_row(name, file_count, code, comment, blank)
199 printf("%-14s", name)
200 printf(" %6d", file_count)
201 printf(" %10d", code)
202 printf(" %10d", comment)
204 printf(" %9.1f%%", comment.to_f / (comment+code).to_f * 100.0)
206 printf(" %10d", blank)
207 printf(" %10d\n", code+comment+blank)
212 STDERR.puts "Error: Multiple commands specified."
223 def set_option(option)
225 when '-s', '--summary'
226 self.subcommand = :summary
227 when '-d', '--detect'
228 self.subcommand = :detect
229 when '-a', '--annotate'
230 self.subcommand = :annotate
231 when '-i', '--individual'
232 self.subcommand = :individual
233 when '-?', '-h', '--help'
234 self.subcommand = :help
236 STDERR.puts "Type 'ohcount -?' for usage."
242 self.subcommand ||= :summary
243 if self.respond_to?(self.subcommand)
244 self.send(self.subcommand)
246 STDERR.puts "Type 'ohcount -?' for usage."
252 OhcountCommandLine.new(ARGV).run!