Xcode script for generating/synthesizing properties

前端 未结 8 1861
南旧
南旧 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 04:04

    This is the one I came up with based on one I found a long time ago, rewritten in Python and with the improvements that it can generate multiple properties at once, among other things.

    It will generate properties for all selected instance variable using (copy) as the attribute.

    There are still some edge cases with multiple @interfaces or @implementations in a file, as well as some with unusual identifiers or asterisk placement (as in *const), but it should cover most typical coding styles. Feel free to edit/post modifications if you fix any of these cases.

    #!/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.
    
    # 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*((?:(?:\w+)\s+)*(?:(?:\w+)))""" + # Identifier(s)
      """([*]?)\\s*""" + # An optional asterisk
      """(\\w+?)(_?);""", # The variable name
      re.M)
    
    # Now for each instance variable in the selected section
    properties = ""
    synthesizes = ""
    
    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 = "(copy) " # Attributes if variable is pointer
        if not asterisk:
          pointerPropertyAttributes = ""
    
        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)
    
        properties += newProperty
        synthesizes += newSynthesize
    
    # 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 synthesize 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])
    
    # Switch Xcode back to header file
    subprocess.Popen(["osascript",
                      "-e",
                      getFileContentsScript,
                      headerFilePath],
                     stdout=subprocess.PIPE).communicate()
    

提交回复
热议问题