Xcode script for generating/synthesizing properties

前端 未结 8 1865
南旧
南旧 2020-12-16 03:03

Does anybody have an Xcode script for generating @property and @synthsize directives for instance variables in a class?

8条回答
  •  庸人自扰
    2020-12-16 03:44

    This is a python script for Xcode 3.2.4 that generates; interface properties, implementation synthesize, and dealloc's. To install, copy this script, go to Xcode scripts menu (2nd to last) "Edit User Scripts..." Add it under Code, create a new script name, and paste the python script below.

    To use just select the variables under the @interface, then call this script. It will then add all of the @property's, in the implementation and all of the @synthesize and dealloc's. It won't add IBOutlet to any of your Labels or Buttons since it doesn't know this, but this is easy to add manually.

    Indentation of the script below is critical so don't change it.

    #!/usr/bin/python
    
    
    # Takes a header file with one or more instance variables selected
    # and creates properties and synthesize directives for the selected properties.
    
    # Accepts google-style instance variables with a tailing underscore and
    # creates an appropriately named property without underscore.
    
    # Xcode script options should be as follows:
    # Entire Document
    # Home Directory
    # Discard Output
    # Display in Alert
    
    import os
    import re
    import subprocess
    
    # AppleScripts for altering contents of files via Xcode
    setFileContentsScript = """\
    on run argv
    set fileAlias to POSIX file (item 1 of argv)
    set newDocText to (item 2 of argv)
    tell application "Xcode"
    set doc to open fileAlias
    set text of doc to newDocText
    end tell
    end run \
    """
    
    getFileContentsScript = """\
    on run argv
    set fileAlias to POSIX file (item 1 of argv)
    tell application "Xcode"
    set doc to open fileAlias
    set docText to text of doc
    end tell
    return docText
    end run \
    """
    
    # Get variables from Xcode
    headerFileText = """%%%{PBXAllText}%%%"""
    selectionStartIndex = %%%{PBXSelectionStart}%%%
    selectionEndIndex = %%%{PBXSelectionEnd}%%%
    selectedText = headerFileText[selectionStartIndex:selectionEndIndex]
    
    headerFilePath = """%%%{PBXFilePath}%%%"""
    
    # Look for an implementation file with .m or .mm extension
    implementationFilePath = headerFilePath[:-1] + "m"
    if not os.path.exists(implementationFilePath):
    implementationFilePath += "m"
    
    instanceVariablesRegex = re.compile(
    """^\s*((?:(?:\\b\w+\\b)\s+)*(?:(?:\\b\\w+\\b)))\\s*""" + # Identifier(s)
    """([*]?)\\s*""" + # An optional asterisk
    """(\\b\\w+?)(_?\\b);""", # The variable name
    re.M)
    
    # Now for each instance variable in the selected section
    properties = ""
    synthesizes = ""
    deallocs = ""
    
    for lineMatch in instanceVariablesRegex.findall(selectedText):
        types = " ".join(lineMatch[0].split()) # Clean up consequtive whitespace
    
        asterisk = lineMatch[1]
        variableName = lineMatch[2]
        trailingUnderscore = lineMatch[3]
    
        pointerPropertyAttributes = "(nonatomic, retain) " # Attributes if variable is pointer
        if not asterisk:
            pointerPropertyAttributes = "(nonatomic, assign) "
    
        newProperty = "@property %s%s %s%s;\n" % (pointerPropertyAttributes,
                                           types,
                                           asterisk,
                                           variableName)
    
        # If there's a trailing underscore, we need to let the synthesize
        # know which backing variable it's using
        newSynthesize = "@synthesize %s%s;\n" % (variableName,
                                         trailingUnderscore and
                                         " = %s_" % variableName)
        # only do the objects
        if asterisk:
            newDealloc = "    [%s%s release];\n" % (variableName,
                        trailingUnderscore and
                                     " = %s_" % variableName)
        properties += newProperty
        synthesizes += newSynthesize
        # only add if it's an object
        if asterisk:
            deallocs += newDealloc
    
    
    # Check to make sure at least 1 properties was found to generate
    if not properties:
        os.sys.stderr.writelines("No properties found to generate")
        exit(-1)
    
    # We want to insert the new properties either immediately after the last
    # existing property or at the end of the instance variable section
    findLastPropertyRegex = re.compile("^@interface.*?{.*?}.*?\\n" +
                             "(?:.*^\\s*@property.*?\\n)?", re.M | re.S)
    headerInsertIndex = findLastPropertyRegex.search(headerFileText).end()
    
    # Add new lines on either side if this is the only property in the file
    addedNewLine = "\n"
    if re.search("^\s*@property", headerFileText, re.M):
        # Not the only property, don't add
        addedNewLine = ""
    
    newHeaderFileText = "%s%s%s%s" % (headerFileText[:headerInsertIndex],
                          addedNewLine,
                          properties,
                          headerFileText[headerInsertIndex:])
    
    subprocess.call(["osascript",
          "-e",
          setFileContentsScript,
          headerFilePath,
          newHeaderFileText])
    
    
    if not os.path.exists(implementationFilePath):
        os.sys.stdout.writelines("No implementation file found")
        exit(0)
    
    implementationFileText = subprocess.Popen(
    ["osascript",
    "-e",
    getFileContentsScript,
    implementationFilePath],
    stdout=subprocess.PIPE).communicate()[0]
    
    # We want to insert the synthesizes either immediately after the last existing
    # @synthesize or after the @implementation directive
    lastSynthesizeRegex = re.compile("^\\s*@implementation.*?\\n" +
                          "(?:.*^\\s*@synthesize.*?\\n)?", re.M | re.S)
    
    implementationInsertIndex = \
    lastSynthesizeRegex.search(implementationFileText).end()
    
    # Add new lines on either side if this is the only synthsize in the file
    addedNewLine = "\n"
    if re.search("^\s*@synthesize", implementationFileText, re.M):
         # Not the only synthesize, don't add
        addedNewLine = ""
    
    newImplementationFileText = "%s%s%s%s" % \
            (implementationFileText[:implementationInsertIndex],
             addedNewLine,
             synthesizes,
             implementationFileText[implementationInsertIndex:])
    
    subprocess.call(["osascript",
           "-e",
           setFileContentsScript,
           implementationFilePath,
           newImplementationFileText])
    
    
    implementationFileText = subprocess.Popen(
    ["osascript",
    "-e",
    getFileContentsScript,
    implementationFilePath],
    stdout=subprocess.PIPE).communicate()[0]
    
    # We want to insert the deallocs either immediately after the last existing
    # [* release] or after the [super dealloc]
    lastDeallocRegex = re.compile("^\\s+\[super dealloc\];?\\n" +
                          "(?:.*^\\s+\[\w release\];?\\n)?", re.M | re.S)
    
    deallocInsertIndex = \
    lastDeallocRegex.search(implementationFileText).end() 
    
    addedNewDeallocLine = "\n"
    if re.search("^\s*\[\w release\];?", implementationFileText, re.M):
    # Not the only dealloc, don't add
    addedNewDeallocLine = ""
    
    
    newImplementationFileText = "%s%s%s%s" % \
             (implementationFileText[:deallocInsertIndex],
              addedNewDeallocLine,
              deallocs,
              implementationFileText[deallocInsertIndex:])
    
    subprocess.call(["osascript",
                  "-e",
                  setFileContentsScript,
                  implementationFilePath,
                  newImplementationFileText])      
    
    # Switch Xcode back to header file
    subprocess.Popen(["osascript",
            "-e",
            getFileContentsScript,
            headerFilePath],
           stdout=subprocess.PIPE).communicate()
    

提交回复
热议问题