Xcode
不得不说,很好用的一款IDE
,他集成了很多功能,但惟独没有发现为一个方法添加注释的功能。尤其是在当有大量的方法需要添加注释,而且注释的格式还要统一的时候,真的让人头疼。在Xcode 3.2
版本的时候,还可以找到appledoc
插件,很方便的生成注释。但是到了Xcode 4.0
以上的版本就找不到这个功能,虽然appledoc
仍然可以用,但是需要使用命令行,而且生成的是html
文件。就没有再仔细研究,继续寻找更简便的方法。最终找到一位大神写的一段ruby
脚本,使用它为系统添加了一项服务,使用此可以很方便为指定的方法生成指定格式的注释。不过,测试发现这段ruby
脚本还是有一点点问题的,在生成注释后会把当前生成注释的方法的声明删掉。我只好凭着多年的编程经验对这段脚本进行了一点修改(第一次接触到ruby
代码。o(╯□╰)o),现在已经很好使用了,基本上没有啥问题了。分享给大家。
先展示个效果:
/** * <#Description#> * * @param application <#application description#> * @param launchOptions <#launchOptions description#> * * @return <#return value description#> */ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { return YES; }
如何安装使用
所需文件:下载1. ruby脚本 Doxygen.rb
原始的 DoxygenNew.rb
我修改的2. 添加服务的应用程序 ThisService.app
首先打开ThisService.app
,加载DoxygenNew.rb
可以通过Add option
增加一些自定义设置,这里只添加了应用程序filter,添加的该服务只有Xcode
能使用
点Test Service
测试服务,可以粘过一些代码过来测试。
测试无误后,添加服务就好了。
然后就可以在Xcode
的服务里找到添加的这个服务了。
为了方便使用再为这个服务设置一个快捷键,往后在使用时,只需要选中要生成注释的方法名,按下快捷键,注释就会自动给生成了。
最后贴上我改过的ruby
代码,希望大家根据自己的需要再进行编辑,拿出来与大家分享。
下载地址
#!/usr/bin/env ruby # # This script helps you make doxygen comments in Obj-C/C/C++ files in XCode # # Created by Fred McCann on 03/16/2010 - and Edwin. # http://www.duckrowing.com # Adapted for ThisService by Martin Pichlmair 03/29/2011 # Modified for Objectiv-c by Dake 07/22/2012 # Modified by wtlucky 11/21/2012 # httip://glade.tk # module Duckrowing # Convenience class to hold name and type information class Argument def initialize(type = nil, name = nil) self.type = type self.name = name end def name @name end def name=(name) if name != nil name.gsub!(/^&/,'') name.gsub!(/^\*/,'') name.gsub!(/\[.*$/,'') name.gsub!(/,$/,'') name.gsub!(/;$/,'') name.gsub!(/^\s*/,'') name.gsub!(/\s*$/,'') end if name == '...' @name = 'vararg_list' else @name = name end end def type @type end def type=(type) if type != nil type.gsub!(/&$/,'') type.gsub!(/\s*\*$/,'') type.gsub!(/^\s*/,'') type.gsub!(/\s*$/,'') end @type = type end end # Base implementation of commenter class BaseCommenter # Creates a new commenter object def initialize(indent, code) @indent = indent @code = code @arguments = [] @returns = false end # Creates an opening comment def start_comment(description = 'Description') str = "/**\n" #str = "#{@indent}/**\n" str += "#{@indent} *\t<##{description}#>\n" str end def arguments_comment str = '' @arguments.each do |arg| if str == '' str += "#{@indent} *\n" end str += "#{@indent} *\t@param \t#{arg.name} \t<##{arg.name} description#>\n" end str end def return_comment return '' if !@returns "#{@indent} *\n#{@indent} *\t@return\t<#return value description#>\n" end # Creates closing comment def end_comment() #"#{@indent} */\n" "#{@indent} */\n#{@indent}" end # Convenience method to detect multiline statements def is_multiline? @code =~ /\n/ end # Adds inline comments to a comma delimited list def comment_list(list, base_indent='') commented_list = "" ids = list.split(/,/) ids.each do |id| id.gsub!(/\s*$/, '') id.gsub!(/^\s*/, '') list_id = "#{id}" list_id += ',' if id != ids.last id.gsub!(/\=.*$/, '') id.gsub!(/\[.*\]/, '') id.gsub!(/\s*$/, '') id.gsub!(/^\s*/, '') id.gsub!(/;/, '') id.gsub!(/\s*\:\s*\d+/,'') doc_id = id.split(/\s/).last doc_id.gsub!(/\*/, '') commented_list += "#{base_indent}" if id != ids.first commented_list += "#{@indent}\t#{list_id} /**< <##{doc_id} description#> */" commented_list += "\n" if id != ids.last end commented_list end # Parses a comma delimited list into an array of Argument objects def parse_c_style_argument_list(str) arguments = [] str.split(/,/).each do |a| arg = Argument.new parts = a.split(/\s+/) arg.name = parts.last parts.delete_at(parts.size - 1) arg.type = parts.join(" ") @arguments << arg end end # Add Xcode selection markup to first editable field def select_first_field(str) # Add PBX selection to first field matches = str.scan(/\<\#.*\#\>/) if matches.size > 0 first_field = matches[0].to_s # str.gsub!(/#{first_field}/, "%%%{PBXSelection}%%%#{first_field}%%%{PBXSelection}%%%") str.gsub!(/#{first_field}/, "#{first_field}") end str end # Returns a comment above the code and the original section of commented code def document str = start_comment() str += arguments_comment() str += return_comment() str += end_comment() str += "#{@code}\n" #print "#{@code}" #matches = @code.scan(/\n/) #print matches.size #if matches.size > 1 #str += "#{@code}" #end select_first_field(str) end end class VariableCommenter < BaseCommenter # Adds a basic comment above individual variables and rewrites multiple # declaritions into an inline commented list def document if @code.gsub(/\n/, ' ') =~ /^([^\{]+\,)/ commented_code = comment_list(@code) commented_code.sub!(/^\s*/,@indent); select_first_field("#{commented_code}") else super end end end class PropertyCommenter < BaseCommenter # Adds a basic comment above individual properties end class MacroCommenter < BaseCommenter # Parse out args for inclusion in comment def capture_args matches = @code.scan(/\(([^\(\)]*)\)/) parse_c_style_argument_list(matches[0].to_s) @returns = true end # Adds a basic comment above individual variables and rewrites multiple # declaritions into an inline commented list def document capture_args if @code =~ /\(/ super end end # Implementation of commenter to comment C style enums class EnumCommenter < BaseCommenter # Comments identifiers in the code block def comment_code block_match = /\{([^\{\}]*)\}/ matches = @code.scan(block_match) return if matches.size != 1 block = matches[0].to_s @code.gsub!(block_match, "{\n#{comment_list(block)}\n#{@indent}}") end # Comments the enum. This will write comments next to each name for a multiline # statement. It will not for single line enumerations. def document comment_code if is_multiline? super end end # Implementation of commenter to comment C style enums class StructCommenter < BaseCommenter # Comments semicolon delimited list of struct members def comment_struct_list(list) commented_list = "" ids = list.gsub(/^\s*/,'').gsub(/\s*$/,'').split(/;/) ids.each do |id| id.gsub!(/\s*$/, '') id.gsub!(/^\s*/, '') list_id = "#{id};" base_indent = "\t" commented_list += "#{comment_list(list_id, base_indent)}\n" end commented_list end # Comments identifiers in the code block def comment_code block_match = /\{([^\{\}]*)\}/ matches = @code.scan(block_match) return if matches.size != 1 block = matches[0].to_s @code.gsub!(block_match, "{\n#{comment_struct_list(block)}#{@indent}}") end # Adds inline comments for members and a comment for the entire struct def document comment_code super end end class FunctionCommenter < BaseCommenter # Parse out args for inclusion in comment def capture_args matches = @code.scan(/\(([^\(\)]*)\)/) parse_c_style_argument_list(matches[0].to_s) end # Decides whether or not to add a returns tag to comment def capture_return @returns = @code.split(/\(/).first !~ /void/ end # Adds a basic comment above individual variables and rewrites multiple # declaritions into an inline commented list def document capture_args capture_return super end end class MethodCommenter < BaseCommenter TAILMATCH = /[\s*;.*]/ # Find the return type def capture_return_type matches = @code.scan(/^\s*[+-]\s*\(([^\(\)]*)\)/) return nil if matches.size != 1 type = matches[0].to_s.gsub(TAILMATCH, '') if type == 'void' || type == 'IBAction' @returns = nil else @returns = type end end # Parse out params def capture_parameters params = [] matches = @code.scan(/\:\(([^\(]+)\)(\S+)/) matches.each do |m| next if m.size != 2 arg = Argument.new arg.type = m[0].to_s.gsub(TAILMATCH, '') arg.name = m[1].to_s.gsub(TAILMATCH, '') @arguments << arg end end # Adds a basic comment above individual variables and rewrites multiple # declaritions into an inline commented list def document capture_parameters capture_return_type super end end class Documenter def document(code) # 此句刷格式缩进了 #code.gsub!(/\s*$/, '') indent = base_indentation(code) klass = nil if is_objc_property?(code) klass = PropertyCommenter elsif is_objc_method?(code) klass = MethodCommenter elsif is_function?(code) klass = FunctionCommenter elsif is_macro?(code) klass = MacroCommenter elsif is_struct?(code) klass = StructCommenter elsif is_union?(code) klass = StructCommenter elsif is_enum?(code) klass = EnumCommenter else klass = VariableCommenter end #puts "USE --> #{klass}" commenter = klass.new(indent, code) commenter.document end private def is_objc_method?(code) code =~ /^\s*[+-]/ end def is_objc_property?(code) code =~ /^\s*\@property/ end def is_function?(code) !is_macro?(code) && !is_objc_method?(code) && code =~ /\(/ end def is_macro?(code) code =~ /^\s*\#define/ end def is_enum?(code) code.gsub(/\n/, ' ') =~ /^\s*(\w+\s)?enum.*\{.*\}/ end def is_struct?(code) code.gsub(/\n/, ' ') =~ /^\s*(\w+\s)?struct.*\{.*\}/ end def is_union?(code) code.gsub(/\n/, ' ') =~ /^\s*(\w+\s)?union.*\{.*\}/ end def base_indentation(code) matches = code.scan(/^(\s*)/) return '' if matches.size == 0 matches[0].to_s end end end # Unicode considerations: # Set $KCODE to 'u'. This makes STDIN and STDOUT both act as containing UTF-8. $KCODE = 'u' # Since any Ruby version before 1.9 doesn't much care for Unicode, # patch in a new String#utf8_length method that returns the correct length # for UTF-8 strings. UNICODE_COMPETENT = ((RUBY_VERSION)[0..2].to_f > 1.8) unless UNICODE_COMPETENT # lower than 1.9 class String def utf8_length i = 0 i = self.scan(/(.)/).length i end end else # 1.9 and above class String alias_method :utf8_length, :length end end input = STDIN.read # input now contains the contents of STDIN. # Write your script here. # Be sure to print anything you want the service to output. documenter = Duckrowing::Documenter.new replacement = documenter.document(input) puts replacement #print replacement #print input