[ create a new paste ] login | about

Project: npppython
Link: http://npppython.codepad.org/JfyTDnMs    [ raw code | fork ]

Python, pasted on Nov 4:
"""
    Notepad++ Python Script script to add setters and getters to C++ classes
    Copyright (C) 2013 George Snyder
    
    This script is free software: you can
    redistribute it and/or modify it under the terms of the GNU
    General Public License (GNU GPL) as published by the Free Software
    Foundation, either version 3 of the License, or (at your option)
    any later version.  The code is distributed WITHOUT ANY WARRANTY;
    without even the implied warranty of MERCHANTABILITY or FITNESS
    FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
    
    As additional permission under GNU GPL version 3 section 7, you
    may distribute non-source (e.g., minimized or compacted) forms of 
    that code without the copy of the GNU GPL normally required by 
    section 4, provided you include this license notice and a URL 
    through which recipients can access the Corresponding Source.
"""

from Npp import notepad,console,editor
import re,pprint

# only deal with C++ files
if (re.match(".*\\.cpp$", notepad.getCurrentFilename())):
    # parse the editor text into statements, blocks (including labeled sections)
    blocks = []
    structure = blocks
    blockPath = [{'children':blocks, 'blockType':'root', 'text': '', 'start':0,'end':editor.getLength()}]
    statementStart = 0
    depth = 0
    inComment = False
    inQuote = False
    quoteChar = ""
    quoteStrings = ["'", '"']
    for i in range(0, editor.getLength()):
        line = editor.getLine(editor.lineFromPosition(i))
        c = "%c" % editor.getCharAt(i)
        if re.match('\\s*(//.*)?$', line): # line is a comment, skip it
            i = editor.getLineEndPosition(editor.lineFromPosition(i))
        elif c == '/' and editor.getCharAt(i+1) == '*':
            inComment = True
        elif inComment and editor.getTextRange(i, i+2) == "*/":
            inComment = False
        elif inComment == True:
            continue
        elif inQuote:
            if c in quoteStrings:
                inQuote = False
        elif c in quoteStrings:
            inQuote = True
            quoteChar = c
        elif c == ';':
            tmp = {'blockType': 'statement', 'text': editor.getTextRange(statementStart, i).strip(), 'start': statementStart, 'end': i}
            blocks.append(tmp)
            statementStart = i+1
        elif c == '{':
            depth += 1
            tmp = {'blockType': 'regular', 'text': editor.getTextRange(statementStart, i).strip(), 'children': [], 'start': statementStart, 'end': -1}
            blocks.append(tmp)
            blockPath.append(tmp)
            blocks = tmp['children']
            statementStart = i+1
        elif c == '}':
            if blockPath[len(blockPath)-1]['blockType'] == 'label':
                terminated = 2
            else:
                terminated = 1
            depth -= terminated
            if depth < 0:
                console.write("Invalid C++, too many closing curly braces at line %d\n" % editor.lineFromPosition(i))
                break
            for j in range(0, terminated):
                blockPath.pop()['end'] = i
            blocks = blockPath[len(blockPath)-1]['children']
            statementStart = i+1
        elif c == ':':
            if blockPath[len(blockPath)-1]['blockType'] != 'label':
                depth += 1
            else:
                blockPath.pop()['end'] = statementStart
                blocks = blockPath[len(blockPath)-1]['children']
            tmp = {'blockType': 'label', 'text': re.match('.*(?:\A|\W)([a-z]\w*):', line).group(1), 'children': [], 'start': statementStart, 'end': -1}
            blocks.append(tmp)
            blockPath.append(tmp)
            blocks = tmp['children']
            statementStart = i+1
    if depth > 0:
        console.write("Invalid C++, too many opening curly braces.\n")
        
    # find the 'class' block that contains our cursor
    currentPosition = editor.getCurrentPos()
    classBlock = None
    for block in structure:
        if block['text'].startswith('class') and block['start'] < currentPosition and block['end'] > currentPosition:
            classBlock = block
            break
    if classBlock == None:
        console.write("You (position %d) don't appear to be inside a class, we don't know which class needs setters!\n" % currentPosition)
    else:
        # find the 'private' and 'public' 'block's in that block
        privateBlock = None
        publicBlock = None
        for block in classBlock['children']:
            if block['blockType'] == 'label':
                if block['text'] == 'private':
                    privateBlock = block
                if block['text'] == 'public':
                    publicBlock = block
        # create a list of all private variable names
        privateVars = []
        if privateBlock != None:
            # find indentation
            indentation = re.match('.*[\n\r]*?([\t ]*)\w.*', editor.getTextRange(privateBlock['start'], privateBlock['end']), flags=re.MULTILINE).group(1)
            indentBase = re.match('.*[\n\r]*?([\t ]*)\w.*', editor.getTextRange(privateBlock['children'][0]['start'], privateBlock['children'][0]['end']), flags=re.MULTILINE).group(1)
            for i in range(0, len(privateBlock['children'])):
                if privateBlock['children'][i]['blockType'] == 'statement':
                    m = re.match('\s*(?P<dataType>\w+)\s+(?P<varName>\w+)\s*', privateBlock['children'][i]['text'])
                    if m:
                        privateVars.append([m.group('dataType'), m.group('varName')])
        else:
            console.write("Could not find the private label")
        # make a list of already created setters and getters
        changes = {}
        existingMethods = []
        if publicBlock != None:
            for i in range(0, len(publicBlock['children'])):
                if publicBlock['children'][i]['blockType'] == 'regular':
                    m = re.match('\s*(?P<datatype>\w+)\s+(?P<methodName>(?P<type>set|get)(?P<varName>\w+))\(.*', publicBlock['children'][i]['text'])
                    if m:
                        existingMethods.append(m.group('datatype') + m.group('methodName'))
        else:
            console.write("Could not find the public label")
        # prepend setters and getters that aren't already created
        if publicBlock != None:
            insertPoint = publicBlock['children'][0]['start']
            changes[insertPoint] = ""
        else:
            insertPoint = privateBlock['end']
            if len(privateVars) == 0:
                changes[insertPoint] = "\r\npublic:" # add public label if it doesn't exist
        for var in privateVars:
            formatVars = {'base': indentBase, 'indent': indentation, 'capName': var[1].capitalize(), 'dType': var[0], 'vName': var[1]}
            if not ("voidset%s" % (var[1].capitalize()) in existingMethods):
                changes[insertPoint] += "\r\n%(base)svoid set%(capName)s(%(dType)s %(vName)s) {\r\n%(base)s%(indent)sthis->%(vName)s = %(vName)s;\r\n%(base)s}" % formatVars
                console.write("Adding setter 'void set%(capName)s(%(dType)s)'\n" % formatVars)
        for var in privateVars:
            formatVars = {'base': indentBase, 'indent': indentation, 'capName': var[1].capitalize(), 'dType': var[0], 'vName': var[1]}
            if not ("%sget%s" % (var[0], var[1].capitalize()) in existingMethods):
                changes[insertPoint] += "\r\n%(base)s%(dType)s get%(capName)s() {\r\n%(base)s%(indent)sreturn %(vName)s\r\n%(base)s}" % formatVars
                console.write("Adding getter '%(dType)s get%(capName)s()'\n" % formatVars)

        # apply changes
        changeOrder = changes.keys()
        changeOrder.sort(reverse=True)
        for key in changeOrder:
            editor.insertText(key, changes[key])
else:
    console.write("Currently only C++ is supported for adding setters and getters.  Might work on other languages if you disable this check. Feel free to modify the script.\n")


Create a new paste based on this one


Comments: