diff --git a/src/tfscript/__init__.py b/src/tfscript/__init__.py index 33c740f..7ed279d 100644 --- a/src/tfscript/__init__.py +++ b/src/tfscript/__init__.py @@ -2,26 +2,48 @@ # Used for the conditions in the type condDict = {} +defaultDict = {} +bindOrAlias = "bind" +from json import dumps +from copy import deepcopy + +def makeCFG(cfg, default=False): + global bindOrAlias + global condDict + global defaultDict + + bindOrAlias = "bind" + if default: + # Write to defaultDict instead of condDict + condDict = defaultDict + else: + condDict = deepcopy(defaultDict) -def makeCFG(cfg): - condDict.clear() ret = '' for key, data in cfg.items(): - # I know all of these fields exist because it was verified in verify.py - bindType = firstTypeIn(data.keys()) - bindContent = data[bindType] - ret += branch(key, bindContent, bindType) + isAlias = False + if "alias" in data: + isAlias = data.pop("alias") + if isAlias: + bindOrAlias = "alias" + else: + bindOrAlias = "bind" + ret += branch(key, data) # Doubles are weird. All of the toggles got put into a dictionary. # This takes all of the nested dictionaries and turns them into the right string for key, toggles in condDict.items(): - ret += f'alias +{key}_toggles "{toggles["plus_toggles"]}"\n' +\ - f'alias -{key}_toggles "{toggles["minus_toggles"]}"\n' +\ - f'bind {key} "+{key}_toggles"\n' + plusToggleStr = ';'.join(toggles["plus_toggles"]) + minusToggleStr = ';'.join(toggles["minus_toggles"]) + ret += f'alias +{key}_toggles "{plusToggleStr}"\n' +\ + f'alias -{key}_toggles "{minusToggleStr}"\n' +\ + f'{bindOrAlias} {key} "+{key}_toggles"\n' + + del condDict # free deep copy return ret -def firstTypeIn(inputList): +def typeOf(dictIn): """ Find the first element common to both lists """ types = [ "impulse", @@ -31,10 +53,13 @@ def firstTypeIn(inputList): "repeat" ] for t in types: - if t in inputList: + if t in dictIn.keys(): return t -def branch(keyName, bindContent, bindType): +def branch(keyName, bindContent): + bindType = typeOf(bindContent) + bindContent = bindContent.pop(bindType) + if bindType == "impulse": return impulse(keyName, bindContent) @@ -54,6 +79,7 @@ def branch(keyName, bindContent, bindType): return repeat(keyName, bindContent) def impulse(key, instruction): + global bindOrAlias if isinstance(instruction, list): instruction = ';'.join(instruction) @@ -64,7 +90,7 @@ def impulse(key, instruction): instruction = ';'.join(allInstructions) - return f'bind {key} "{instruction}"\n' + return f'{bindOrAlias} {key} "{instruction}"\n' def impulseShortcuts(instruction): splitCommand = instruction.split(' ') @@ -116,13 +142,15 @@ def expandBuildings(building): return num def simpleHold(key, instruction): + global bindOrAlias # This isn't quite right, fix later! - if instruction[0] != '+': - return f'bind {key} "+{instruction}"\n' + if instruction[0] == '+' or instruction[0] == '-': + return f'{bindOrAlias} {key} "{instruction}"\n' else: - return f'bind {key} "{instruction}"\n' + return f'{bindOrAlias} {key} "+{instruction}"\n' def listHold(key, options): + global bindOrAlias pressStr = options["press"] if isinstance(pressStr, list): pressStr = ';'.join(pressStr) @@ -133,10 +161,12 @@ def listHold(key, options): ret = f'alias +{key}_bind "{pressStr}"\n' +\ f'alias -{key}_bind "{releaseStr}"\n' +\ - f'bind {key} "+{key}_bind"\n' + f'{bindOrAlias} {key} "+{key}_bind"\n' + return ret def toggle(key, instruction): + global bindOrAlias onStr = f'turn_{key}_on' offStr = f'turn_{key}_off' togStr = f'toggle_{key}' @@ -144,59 +174,47 @@ def toggle(key, instruction): ret = f'alias {onStr} "+{instruction}; alias {togStr} {offStr}"\n' +\ f'alias {offStr} "-{instruction}; alias {togStr} {onStr}"\n' +\ f'alias {togStr} "{onStr}"\n' +\ - f'bind {key} "{togStr}"\n' + f'{bindOrAlias} {key} "{togStr}"\n' return ret def double(key, options): primaryAction = options["primary"] - pBindType = firstTypeIn(primaryAction.keys()) - pBindContent = primaryAction[pBindType] secAction = options["secondary"] - sBindType = firstTypeIn(secAction.keys()) - sBindContent = secAction[sBindType] mainStr = f'{key}_main' altStr = f'{key}_alt' - togStr = f'toggle_{key}' + pTogStr = f'+toggle_{key}' + mTogStr = f'-toggle_{key}' - recursiveCode = branch(mainStr, pBindContent, pBindType) +\ - branch(altStr, sBindContent, sBindType) + global bindOrAlias + oldBindOrAlias = bindOrAlias + bindOrAlias = "alias" + recursiveCode = branch(mainStr, primaryAction) +\ + branch(altStr, secAction) + bindOrAlias = oldBindOrAlias - newcode = [] - for line in recursiveCode.split('\n'): - # For every line gotten by the recursive call, change all "bind"s to "alias", - # since mainStr and altStr aren't valid bind targes - llist = line.split(' ') - for i in range(len(llist)): - alphanumChars = ''.join(c for c in llist[i] if c.isalnum()) - if alphanumChars == 'bind': - if llist[i][0].isalnum(): - llist[i] = 'alias' - else: - # If the first character isn't a normal character. - # Almost always because it is a double quote - llist[i] = llist[i][0] + 'alias' - newcode.append(' '.join(llist)) - - ret = '\n'.join(newcode) +\ - f'alias +{togStr} "bind {key} {altStr}"\n' +\ - f'alias -{togStr} "bind {key} {mainStr}"\n'+\ - f'bind {key} "{mainStr}"\n' + ret = recursiveCode +\ + f'alias {pTogStr} "{bindOrAlias} {key} {altStr}"\n' +\ + f'alias {mTogStr} "{bindOrAlias} {key} {mainStr}"\n'+\ + f'{bindOrAlias} {key} "{mainStr}"\n' condName = options["condition"] - + global condDict if condName in condDict: # If the condition key (like "mouse4") already has toggles, - # just append another toggle string (it gets encased in quotes later) - condDict[condName]["plus_toggles"] += f"; +{togStr}" - condDict[condName]["minus_toggles"] += f"; -{togStr}" + # just append another toggle string + plusToggles = condDict[condName]["plus_toggles"] + minusToggles = condDict[condName]["minus_toggles"] + if pTogStr not in plusToggles: + plusToggles.append(pTogStr) + minusToggles.append(mTogStr) else: - # If the condition key doesn't already exist, make it with the correct values + # If the condition key doesn't already exist, make it condDict.update( { condName: { - "plus_toggles": f"+{togStr}", - "minus_toggles": f"-{togStr}" + "plus_toggles": [ pTogStr ], + "minus_toggles": [ mTogStr ] } } ) diff --git a/src/tfscript/verify.py b/src/tfscript/verify.py index ceb8e41..0ee9d5f 100644 --- a/src/tfscript/verify.py +++ b/src/tfscript/verify.py @@ -3,18 +3,22 @@ def verifyConfig(cfg: dict) -> (dict, dict): verifiedConfig = {} # Do defaults first - aliasErrors = [] + errors = {} defaults = None if "default" in cfg: defaults = cfg.pop("default") + errMessages = [] for key, data in defaults.items(): - isAlias = ("alias" in data and data["alias"] == True) - errMessages = validBind(key, data, alias = isAlias) - if len(errMessages) > 0: - for msg in errMessages: - aliasErrors.append(f"Error in defaults: {msg}") + isAlias = False + if "alias" in data: + isAlias = data["alias"] + if not isinstance(isAlias, bool): + errMessages.append(f'"alias" field in "{key}" makes no sense: "{isAlias}"') + errMessages.extend(validBind(key, data, alias = isAlias) ) + if len(errMessages) > 0: + errors.update( {"default": errMessages} ) classList = [ "scout", @@ -28,9 +32,6 @@ def verifyConfig(cfg: dict) -> (dict, dict): "spy" ] - errors = aliasErrors.copy() - - for cclass in classList: classCFG = None className = cclass @@ -47,33 +48,35 @@ def verifyConfig(cfg: dict) -> (dict, dict): # It may be less efficient this way, but # it makes for more descriptive error messages continue + errMessages = [] for key, data in classCFG.items(): - errMessages = [] isAlias = False if "alias" in data: isAlias = data["alias"] if not isinstance(isAlias, bool): - errMessages.append(f'Key "{key}" has alias not set to true or false. Did you accidentally put it in quotes?') + errMessages.append(f'"alias" field in "{key}" makes no sense: "{isAlias}"') errMessages.extend( validBind(key, data, alias = isAlias) ) - if len(errMessages) > 0: - for msg in errMessages: - errors.append(f"Error in {cclass}: {msg}") + if len(errMessages) > 0: + errors.update( {className: errMessages} ) verifiedConfig.update({className: classCFG}) # Turn list into only strings by expanding tuples for i, clss in enumerate(classList): if isinstance(clss, tuple): - classList.insert(i+1, clss[0]) classList.insert(i+1, clss[1]) + classList.insert(i+1, clss[0]) classList.pop(i) + globalErrors = [] for remainingClass in cfg: if remainingClass not in classList: - errors.append(f'Error in {remainingClass}: "{remainingClass}" is not a valid class') + globalErrors.append(f'"{remainingClass}" is not a valid class') else: otherName = findTwin(remainingClass) - if otherName is not None: - errors.append(f'Error in {remainingClass}: conflicting names for section: "{remainingClass}" and "{otherName}"') + globalErrors.append(f'Conflicting names for section: "{remainingClass}" and "{otherName}"') + + if len(globalErrors) > 0: + errors.update( {"file": globalErrors} ) if len(errors) > 0: verifiedConfig.update({"errors": errors}) @@ -95,8 +98,8 @@ def validBind(key, data, alias = False) -> list: extras = dataCopy.keys() if len(extras) > 0: - extrasString = "\n\t".join(extras) - ret.append(f'Unused fields in "{key}":\n\t{extrasString}') + extrasString = "\n ".join(extras) + ret.append(f'Unused fields in "{key}":\n {extrasString}') return ret @@ -108,7 +111,10 @@ validKeyList = [ 'u', 'v', 'w', 'x', 'y', 'z', 'mouse1', 'mouse2', 'mouse3', 'mouse4', 'mouse5', 'shift', 'capslock', 'ctrl', 'semicolon', 'space', 'enter', - 'backspace' + 'backspace', + 'scrolllock', 'numlock', + 'ins', 'home', 'pgup', + 'del', 'end', 'pgdn' ] def validKey(key): @@ -144,6 +150,7 @@ def validBindType(key, data: dict): def removeRelaventFields(data, bindType): errMsgs = [] + if "alias" in data: data.pop("alias") # These types are simple, just the bind type and argument if bindType in ["impulse", "toggle"]: data.pop(bindType)