Created
October 1, 2010 20:42
-
-
Save jchatard/606822 to your computer and use it in GitHub Desktop.
Modified version of Fred McCann doxygen User script to add Doxygen comments in Objective-C
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/ruby | |
# | |
# This script helps us make doxygen comments in obj-c files in Xcode | |
# | |
# Created by Fred McCann on 03/16/2010. | |
# http://www.duckrowing.com | |
# | |
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 = 'Brief description', long_description = 'More detailed description...') | |
str = "#{@indent}/**\n" | |
str += "#{@indent} * <##{description}#>\n" | |
str += "#{@indent} *\n" | |
str += "#{@indent} * <##{long_description}#>\n" | |
str | |
end | |
def arguments_comment | |
str = '' | |
if @arguments.count > 0 | |
str = "#{@indent} *\n" | |
end | |
@arguments.each do |arg| | |
str += "#{@indent} * @param #{arg.name} <##{arg.name} description#>\n" | |
end | |
str | |
end | |
def return_comment | |
return '' if !@returns | |
str = "#{@indent} *\n" | |
str += "#{@indent} * @returns <#return value description#>\n" | |
end | |
# Creates closing comment | |
def end_comment() | |
"#{@indent} */\n" | |
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} #{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}%%%") | |
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" | |
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}\n") | |
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 = " * " | |
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 | |
documenter = Duckrowing::Documenter.new | |
code = STDIN.read | |
replacement = documenter.document(code) | |
puts replacement |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment