From 856a83197bd194f98042a12700f724aa6dc5e98c Mon Sep 17 00:00:00 2001 From: Vladimir Schneider Date: Tue, 11 Apr 2023 20:40:21 -0400 Subject: [PATCH] change code to use command argument class --- VERSION.md | 12 +- resources/META-INF/plugin.xml | 6 +- .../cmake/ArduinoCMakeListsTxtBuilder.kt | 24 ++- .../generators/cmake/CMakeListsTxtBuilder.kt | 129 +++++++-------- .../cmake/commands/CMakeCommand.java | 15 +- .../cmake/commands/CMakeCommandBase.java | 151 +++++++----------- .../cmake/commands/CMakeUnknownCommand.java | 12 +- .../cmake/commands/CommandArgument.java | 104 ++++++++++++ .../cmake/ArduinoCMakeBuilderTest.kt | 2 +- 9 files changed, 260 insertions(+), 195 deletions(-) create mode 100644 src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CommandArgument.java diff --git a/VERSION.md b/VERSION.md index 78a284a..51a9d4d 100644 --- a/VERSION.md +++ b/VERSION.md @@ -5,8 +5,9 @@ ### Version History - [CLionArduinoPlugin Version Notes](#clionarduinoplugin-version-notes) - [TO DO](#to-do) - - [1.5.32](#1532) + - [1.5.33](#1533) - [1.5.31](#1531) + - [1.5.29](#1529) - [1.5.25](#1525) - [1.5.11](#1511) - [1.5.9](#159) @@ -54,12 +55,17 @@ * Add: inspection to check that `CMakeLists.txt` is out of sync, especially if missing the toolchain file. -### 1.5.32 +### 1.5.33 -* Fix: improve toolchain file path error and info messages +* Fix: refactor cmake AST command with `CommandArgument` class to encapsulate the value and + leading spaces instead of lugging around two separate lists. ### 1.5.31 +* Fix: improve toolchain file path error and info messages + +### 1.5.29 + * Fix: revert macro name for `_SRCS` and `_HRDS` to be resolved, with arguments to remain unresolved. diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index 6818433..241e4e7 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -4,7 +4,7 @@ Arduino Support Vladimir Schneider - 1.5.31 + 1.5.33 com.intellij.modules.clion @@ -52,8 +52,10 @@ ]]> 1.5.32 +

1.5.34

    +
  • Fix: refactor cmake AST command with CommandArgument class to encapsulate the value and + leading spaces instead of lugging around two separate lists.
  • Fix: improve toolchain file path error and info messages
  • Fix: revert macro name for _SRCS and _HRDS to be resolved, with arguments to remain unresolved. diff --git a/src/com/vladsch/clionarduinoplugin/generators/cmake/ArduinoCMakeListsTxtBuilder.kt b/src/com/vladsch/clionarduinoplugin/generators/cmake/ArduinoCMakeListsTxtBuilder.kt index 8d2cd93..94989f3 100644 --- a/src/com/vladsch/clionarduinoplugin/generators/cmake/ArduinoCMakeListsTxtBuilder.kt +++ b/src/com/vladsch/clionarduinoplugin/generators/cmake/ArduinoCMakeListsTxtBuilder.kt @@ -49,9 +49,6 @@ import java.io.File * generate_arduino_firmware(${CMAKE_PROJECT_NAME}) */ class ArduinoCMakeListsTxtBuilder : CMakeListsTxtBuilder { - - constructor() : super(ourCommands, ourAnchors, PROJECT_NAME) - constructor(text: CharSequence, options: DataHolder? = null, values: Map? = null) : super( PROJECT_NAME, ourCommands, @@ -222,7 +219,7 @@ class ArduinoCMakeListsTxtBuilder : CMakeListsTxtBuilder { builder.setOrAddCommand(SET_HDRS, hFiles).setSuppressible(hFiles.isEmpty()) // TODO: implement - val staticLibs = arrayOf(); + val staticLibs = arrayOf() builder.setOrAddCommand(SET_LIBS, *staticLibs).setSuppressible(staticLibs.isEmpty()) builder.setOrAddCommand(SET_SKETCH, sketchFile.ifEmpty(projectName + Strings.DOT_INO_EXT)).setSuppressible(sketchFile.isEmpty()) @@ -271,7 +268,7 @@ class ArduinoCMakeListsTxtBuilder : CMakeListsTxtBuilder { } // Can add our own values to resolve variables - + return builder.getCMakeContents( null, !appSettings.isCommentUnusedSettings, @@ -304,15 +301,15 @@ class ArduinoCMakeListsTxtBuilder : CMakeListsTxtBuilder { builder.isWantCommented = false // commented commands don't count // see if at all our project type by looking for generate_arduino_firmware(${CMAKE_PROJECT_NAME}) or generate_arduino_library(${CMAKE_PROJECT_NAME}) - val arduinoCommand = builder.getCommand(ArduinoCMakeListsTxtBuilder.GENERATE_ARDUINO_FIRMWARE) - ?: builder.getCommand(ArduinoCMakeListsTxtBuilder.GENERATE_ARDUINO_LIBRARY) ?: return null + val arduinoCommand = builder.getCommand(GENERATE_ARDUINO_FIRMWARE) + ?: builder.getCommand(GENERATE_ARDUINO_LIBRARY) ?: return null // ok, it is ours val settings = ArduinoApplicationSettingsProxy.wrap(applicationSettings, false) as ArduinoProjectFileSettings val cMakeVariableValues = builder.cMakeVariableValues val cMakeProjectName = builder.cMakeProjectName - settings.projectName = cMakeProjectName ?: "" + settings.projectName = cMakeProjectName settings.sources = cMakeVariableValues["${cMakeProjectName}_SRCS"].toTypedArray() settings.headers = cMakeVariableValues["${cMakeProjectName}_HDRS"].toTypedArray() settings.sketch = cMakeVariableValues["${cMakeProjectName}_SKETCH"].firstOrNull() ?: "" @@ -331,12 +328,12 @@ class ArduinoCMakeListsTxtBuilder : CMakeListsTxtBuilder { } when (arduinoCommand.commandType) { - ArduinoCMakeListsTxtBuilder.GENERATE_ARDUINO_LIBRARY -> { + GENERATE_ARDUINO_LIBRARY -> { settings.isLibrary = true settings.libraryType = ArduinoProjectFileSettings.STATIC_LIB_TYPE } - ArduinoCMakeListsTxtBuilder.GENERATE_ARDUINO_FIRMWARE -> { + GENERATE_ARDUINO_FIRMWARE -> { if (libraryProperties.exists() && libraryProperties.isFile) { settings.isLibrary = true settings.libraryType = ArduinoProjectFileSettings.ARDUINO_LIB_TYPE @@ -351,14 +348,15 @@ class ArduinoCMakeListsTxtBuilder : CMakeListsTxtBuilder { val list = builder.getCommands(LINK_DIRECTORIES).flatMap { it.args } if (!list.isEmpty()) { settings.isAddLibraryDirectory = true - settings.libraryDirectories = list.map { it.removePrefix("\${CMAKE_CURRENT_SOURCE_DIR}/") }.toTypedArray() + settings.libraryDirectories = + list.map { it.value.toString().removePrefix("\${CMAKE_CURRENT_SOURCE_DIR}/") }.toTypedArray() } } if (!settings.isLibrary) { val list = ArrayList() - builder.getCommands(ArduinoCMakeListsTxtBuilder.SET_LIB_NAMES_RECURSE).forEach { - list.addAll(it.args) + builder.getCommands(SET_LIB_NAMES_RECURSE).forEach { command -> + list.addAll(command.args.map { arg -> arg.value.toString() }) } settings.nestedLibraries = list.toTypedArray() } diff --git a/src/com/vladsch/clionarduinoplugin/generators/cmake/CMakeListsTxtBuilder.kt b/src/com/vladsch/clionarduinoplugin/generators/cmake/CMakeListsTxtBuilder.kt index 2954407..153a2fb 100644 --- a/src/com/vladsch/clionarduinoplugin/generators/cmake/CMakeListsTxtBuilder.kt +++ b/src/com/vladsch/clionarduinoplugin/generators/cmake/CMakeListsTxtBuilder.kt @@ -191,7 +191,7 @@ abstract class CMakeListsTxtBuilder( // RELEASE: if inside conditional or looping commands then do not replace its macro values // by checking the parent nodes (or previous nodes) and seeing if unmatched parent conditional or loop is our parent val args: List = rawArgs.map { cMakeVariableValues.resolve(it) } - var useVarName: String? = null + var useVarName: String? if (rawArgs.isNotEmpty() && args.size > 1 && (rawArgs[0] == "\${CMAKE_PROJECT_NAME}" && args[0].isEmpty() || rawArgs[0] == "\${PROJECT_NAME}") && args[0].isEmpty()) { // probably a mistake of including ${} around variable name @@ -256,19 +256,18 @@ abstract class CMakeListsTxtBuilder( fun elementFrom(node: Node, valueSet: Map?): CMakeElement { // either command or text if (node is Command) { - // find the command or we can create a new one if we don't have it already or just make it into a text block + // find the command or create a new one if we don't have it already or just make it into a text block var commandType: CMakeCommandType? = null - val rawArgs = ArrayList() - val rawArgsLeadingSpaces = ArrayList() + val rawArgs = ArrayList() for (arg in node.getChildren()) { if (arg is Argument) { - rawArgs.add(arg.text.toString()) - if (arg.previous is LineEnding) { - rawArgsLeadingSpaces.add(arg.leadingSpaces.prefixWithEOL()) - } else { - rawArgsLeadingSpaces.add(arg.leadingSpaces) - } + rawArgs.add( + CommandArgument.of( + arg.text, + if (arg.previous is LineEnding) arg.leadingSpaces.prefixWithEOL() else arg.leadingSpaces + ) + ) } } @@ -282,25 +281,25 @@ abstract class CMakeListsTxtBuilder( // see if we have a matching set command val arg = rawArgs.firstOrNull() if (arg != null) { - val setCommand: String? - - if (!canUseUnmodifiedOriginal && rawArgs.isNotEmpty() && rawArgs.size > 1 && (rawArgs[0] == "\${CMAKE_PROJECT_NAME}" || rawArgs[0] == "\${PROJECT_NAME}")) { + val setCommand: String = if (!canUseUnmodifiedOriginal && rawArgs.isNotEmpty() && rawArgs.size > 1 + && (rawArgs[0].isEqualValue("\${CMAKE_PROJECT_NAME}") || rawArgs[0].isEqualValue("\${PROJECT_NAME}")) + ) { // erroneous set, we convert it to proper set project name - setCommand = cMakeProjectNameMacro.removeSurrounding("\${", "}") + cMakeProjectNameMacro.removeSurrounding("\${", "}") } else { - setCommand = arg + arg.value.toString() } // normalize it to PROJECT_NAME as commands expect - val resolvedSetCommand: String - if (setCommand == "\${PROJECT_NAME}_SRCS" || setCommand == "\${PROJECT_NAME}_HDRS") { - // do not resolve - resolvedSetCommand = cMakeVariableValues[setCommand].joinToString { it } - } else { - resolvedSetCommand = cMakeVariableValues.resolve(setCommand) - } + val resolvedSetCommand: String = + if (setCommand == "\${PROJECT_NAME}_SRCS" || setCommand == "\${PROJECT_NAME}_HDRS") { + // do not resolve + cMakeVariableValues[setCommand].joinToString { it } + } else { + cMakeVariableValues.resolve(setCommand) + } - rawArgs[0] = resolvedSetCommand + rawArgs[0] = rawArgs[0].withValue(resolvedSetCommand) commandType = mySetCommandsArg0[setCommand] if (commandType == null) { @@ -313,7 +312,7 @@ abstract class CMakeListsTxtBuilder( if (converted.replace(projectNameMacro, cMakeProjectName) == resolvedSetCommand) { // it is this commandType = mySetCommandsArg0[name] - rawArgs[0] = name + rawArgs[0] = rawArgs[0].withValue(name) break } else if (converted.indexOf(CMakeCommandType.WILDCARD_ARG_MARKER) >= 0) { // has wildcard match @@ -336,11 +335,6 @@ abstract class CMakeListsTxtBuilder( val makeCommand: CMakeCommandBase - // DEBUG: remove lines - if (commandName == "made_up_command") { - val tmp = 0 - } - if (commandType != null) { makeCommand = CMakeCommand(commandType, false, addBeforeClosing) makeCommand.leadingSpaces = node.leadingSpaces @@ -353,21 +347,21 @@ abstract class CMakeListsTxtBuilder( if (fixedArg.contains(CMakeCommandType.WILDCARD_ARG_MARKER)) { // extract value of wild card val regEx = Pattern.compile("^\\Q" + fixedArg.replace(CMakeCommandType.WILDCARD_ARG_MARKER, "\\E(.*?)\\Q") + "\\E$") - val matcher = regEx.matcher(rawArgs[i++]) + val matcher = regEx.matcher(rawArgs[i++].value) matcher.find() for (g in 1 .. matcher.groupCount()) { - makeCommand.setArg(j++, matcher.group(g), rawArgsLeadingSpaces[g]) + makeCommand.setOrAddArg(j++, rawArgs[g].withValue(matcher.group(g))) } } else i++ } while (i < rawArgs.size) { - makeCommand.setArg(j++, rawArgs[i], rawArgsLeadingSpaces[i]) + makeCommand.setOrAddArg(j++, rawArgs[i]) i++ } } else { // unknown command - makeCommand = CMakeUnknownCommand(commandName, rawArgs, rawArgsLeadingSpaces, false, addBeforeClosing) + makeCommand = CMakeUnknownCommand(commandName, rawArgs, false, addBeforeClosing) makeCommand.leadingSpaces = node.leadingSpaces } @@ -519,7 +513,7 @@ abstract class CMakeListsTxtBuilder( if (commandType == SET_PROJECT_NAME && commandType.isOfType(element.commandType)) { // specialization, compare first argument to see if a match, for now just set project name val setCommandMacro = outputCMakeProjectNameMacro.ifEmpty { cMakeProjectNameMacro } - if (element.argCount > 1 && element.arg(0).prefixWith("\${").suffixWith("}") == setCommandMacro) { + if (element.argCount > 1 && element.arg(0).value.prefixWith("\${").suffixWith("}").equals(setCommandMacro)) { return i } } @@ -694,11 +688,11 @@ abstract class CMakeListsTxtBuilder( val beforeDependents = myBeforeAnchorsMap[command.commandType] val afterDependents = myAfterAnchorsMap[command.commandType] if (beforeDependents != null) { - // treat them as first (ie. before this node) + // treat them as first (i.e. before this node) adjustIndexRange(command, range, beforeDependents, AnchorType.FIRST, false) } if (afterDependents != null) { - // treat them as last (ie. after this node) + // treat them as last (i.e. after this node) adjustIndexRange(command, range, afterDependents, AnchorType.LAST, false) } } else { @@ -740,41 +734,39 @@ abstract class CMakeListsTxtBuilder( if (command.isOfType(PROJECT) && command.argCount > 0) { // copy its argument for generating the CMakeLists file - outputCMakeProjectNameMacro = command.arg(0) + outputCMakeProjectNameMacro = command.arg(0).value.toString() } if (range.beforeIndex == myElements.size && range.afterIndex == 0) { // no anchors, goes last addElement(command) } else if (range.beforeIndex == myElements.size) { - // no before anchor, goes after + // not before anchor, goes after addElement(range.afterIndex, command) } else if (range.afterIndex == 0) { - // no after anchor, goes before + // not after anchor, goes before addElement(range.beforeIndex, command) } else { - // if (!afterHasPriority && range.beforeIndex < range.afterIndex) throw IllegalStateException("Invalid anchor definitions: BeforeAnchor(" + range.beforeIndex + ") < AfterAnchor(" + range.afterIndex + ") for " + command.commandType.name) // insert before addElement(if (afterHasPriority) range.afterIndex else range.beforeIndex, command) } } fun setCommand(name: String, args: Collection): CMakeCommand? { - return setCommand(getCommandType(name), args) - } - - fun setOrAddCommand(name: String, vararg args: String): CMakeCommand { - val commandType = getCommandType(name) - return setOrAddCommand(commandType!!, Arrays.asList(*args)) + return setCommand(getCommandType(name), args.map { CommandArgument.of(it) }) } fun setOrAddCommand(name: String, args: Collection): CMakeCommand { val commandType = getCommandType(name) - return setOrAddCommand(commandType!!, args) + return setOrAddCommand(commandType!!, args.map { CommandArgument.of(it) }) } fun setOrAddCommand(commandType: CMakeCommandType, vararg args: String): CMakeCommand { - return setOrAddCommand(commandType, Arrays.asList(*args)) + return setOrAddCommand(commandType, listOf(*args).map { CommandArgument.of(it) }) + } + + fun setOrAddCommand(commandType: CMakeCommandType, args: Collection): CMakeCommand { + return setOrAddCommand(commandType, args.map { CommandArgument.of(it) }) } fun removeCommand(name: String) { @@ -798,11 +790,11 @@ abstract class CMakeListsTxtBuilder( * @param args arguments for the command * @return resulting command (new or modified) */ - fun setOrAddCommand(commandType: CMakeCommandType, args: Collection?): CMakeCommand { + fun setOrAddCommand(commandType: CMakeCommandType, args: List?): CMakeCommand { val command = setCommand(commandType, args) if (command == null) { // create a new one and find where to insert it - val newCommand = CMakeCommand(commandType, args?.toList() ?: listOf(), null, true, BasedSequence.NULL) + val newCommand = CMakeCommand(commandType, args?.toList() ?: listOf(), true, BasedSequence.NULL) addCommand(newCommand) return newCommand } @@ -813,14 +805,14 @@ abstract class CMakeListsTxtBuilder( * Replace or modify a command based on command type and command anchors information * if the command does not exist in the builder then nothing is done and null is returned. * - * RELEASE: have to not resolve its args to values if it is inside if(), else(), foreach(), while(), function() + * FIX: setOrAddCommand has to not resolve its args to values if it is inside if(), else(), foreach(), while(), function() * currently, expansion proceeds with the last set value * * @param commandType command name (not CMake command name but builder specific name) * @param args arguments for the command * @return resulting command (new or modified) */ - fun setCommand(commandType: CMakeCommandType?, args: Collection?): CMakeCommand? { + fun setCommand(commandType: CMakeCommandType?, args: Collection?): CMakeCommand? { val command = getCommand(commandType) var newCommand = command @@ -832,10 +824,10 @@ abstract class CMakeListsTxtBuilder( // original command, replace it with new newCommand = CMakeCommand(command) newCommand.commentOut(false) - newCommand.setArgsWithDefaults(argList, command.getArgsLeadingSpaces(argList)) + newCommand.setArgsWithDefaults(command.getArgsWithLeadingSpaces(argList)) replaceElement(command, newCommand) } else { - newCommand!!.setArgsWithDefaults(argList, null) + newCommand!!.setArgsWithDefaults(argList) } } else { if (commandType.isMultiple) { @@ -844,8 +836,7 @@ abstract class CMakeListsTxtBuilder( if (!command.allArgsEqual(argList)) { newCommand = CMakeCommand( command.commandType, - argList, - command.getArgsLeadingSpaces(argList), + command.getArgsWithLeadingSpaces(argList), true, command.addBeforeClosing ) @@ -854,13 +845,13 @@ abstract class CMakeListsTxtBuilder( } } else { // add another one after this one - newCommand = CMakeCommand(command.commandType, argList, null, true, BasedSequence.NULL) + newCommand = CMakeCommand(command.commandType, argList, true, BasedSequence.NULL) addElementAfter(command, newCommand) } } else { if (command.isOfType(PROJECT) && argList.isNotEmpty()) { // copy its argument for generating the CMakeLists file - outputCMakeProjectNameMacro = argList[0] + outputCMakeProjectNameMacro = argList[0].value.toString() } if (myElementNodeMap[command] != null) { @@ -868,18 +859,18 @@ abstract class CMakeListsTxtBuilder( if (command.isOfType(commandType)) { newCommand = CMakeCommand(command) newCommand.commentOut(false) - newCommand.setArgsWithDefaults(argList, command.getArgsLeadingSpaces(argList)) + newCommand.setArgsWithDefaults(command.getArgsWithLeadingSpaces(argList)) replaceElement(command, newCommand) } else { - // is a matched sub-type, need to replace completely - newCommand = CMakeCommand(commandType, argList, null, false, command.addBeforeClosing) + // is a matched subtype, need to replace completely + newCommand = CMakeCommand(commandType, argList, false, command.addBeforeClosing) newCommand.commentOut(false) - newCommand.setArgsWithDefaults(argList, command.getArgsLeadingSpaces(argList)) + newCommand.setArgsWithDefaults(command.getArgsWithLeadingSpaces(argList)) replaceElement(command, newCommand) } } else { - newCommand!!.setArgsWithDefaults(argList, null) + newCommand!!.setArgsWithDefaults(argList) } } } @@ -893,11 +884,7 @@ abstract class CMakeListsTxtBuilder( if (values != null) valueSet.putAll(values) if (outputCMakeProjectNameMacro.isEmpty()) { - if (cMakeProjectNameMacro.isEmpty()) { - outputCMakeProjectNameMacro = projectNameMacro - } else { - outputCMakeProjectNameMacro = cMakeProjectNameMacro - } + outputCMakeProjectNameMacro = cMakeProjectNameMacro.ifEmpty { projectNameMacro } } // if cmake project var name not given, set it to the @@ -1083,7 +1070,7 @@ abstract class CMakeListsTxtBuilder( // @formatter:on val ourIndentingCommands = ourAllCommands.filter { it.isPostIndenting || it.isPreUnIndenting } - val ourIndentingCommandMap: Map = ourIndentingCommands.map { it.name to it }.toMap() + val ourIndentingCommandMap: Map = ourIndentingCommands.associateBy { it.name } private val LOG = Logger.getInstance("com.vladsch.clionarduinoplugin.generators") private val DEFAULT_OPTIONS = MutableDataSet() @@ -1110,7 +1097,7 @@ abstract class CMakeListsTxtBuilder( * @return string with variables replaced */ @Suppress("MemberVisibilityCanBePrivate") - fun replacedCommandParams(arg: String, valueSet: Map?): String { + fun replacedCommandParams(arg: CharSequence, valueSet: Map?): String { val result = arg.resolveRefs(TemplateResolver.COMMAND_REF) { name, index -> val ref = valueSet?.get(name) if (ref == null) null @@ -1121,7 +1108,7 @@ abstract class CMakeListsTxtBuilder( var varIndex = index?.toIntOrNull() ?: 0 if (varIndex < 0) varIndex += command!!.argCount - if (varIndex >= 0 && varIndex < command!!.argCount) command.getArg(varIndex) else "" + if (varIndex >= 0 && varIndex < command!!.argCount) command.getArg(varIndex).value.toString() else "" } } return result diff --git a/src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CMakeCommand.java b/src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CMakeCommand.java index 41f85c0..14dd676 100644 --- a/src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CMakeCommand.java +++ b/src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CMakeCommand.java @@ -2,27 +2,26 @@ import com.vladsch.flexmark.util.sequence.BasedSequence; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.List; public class CMakeCommand extends CMakeCommandBase { - - public CMakeCommand(@NotNull final CMakeCommandType commandType, @NotNull final List args, @Nullable final List argsLeadingSpaces, boolean isAddEOL, boolean commented, boolean suppressibleCommented, @NotNull BasedSequence addEolBeforeClosing) { - super(commandType, args, argsLeadingSpaces, isAddEOL, commented, suppressibleCommented, addEolBeforeClosing); + + public CMakeCommand(@NotNull final CMakeCommandType commandType, @NotNull final List args, boolean isAddEOL, boolean commented, boolean suppressibleCommented, @NotNull BasedSequence addEolBeforeClosing) { + super(commandType, args, isAddEOL, commented, suppressibleCommented, addEolBeforeClosing); } - public CMakeCommand(@NotNull final CMakeCommandType commandType, @NotNull final List args, @Nullable final List argsLeadingSpaces, boolean isAddEOL, @NotNull BasedSequence addEolBeforeClosing) { - this(commandType, args, argsLeadingSpaces, isAddEOL, false, false, addEolBeforeClosing); + public CMakeCommand(@NotNull final CMakeCommandType commandType, @NotNull final List args, boolean isAddEOL, @NotNull BasedSequence addEolBeforeClosing) { + this(commandType, args, isAddEOL, false, false, addEolBeforeClosing); } public CMakeCommand(@NotNull final CMakeCommandType commandType, boolean isAddEOL, @NotNull BasedSequence addEolBeforeClosing) { - this(commandType, Collections.emptyList(), Collections.emptyList(), isAddEOL, false, false, addEolBeforeClosing); + this(commandType, Collections.emptyList(), isAddEOL, false, false, addEolBeforeClosing); } public CMakeCommand(@NotNull final CMakeCommand other) { - this(other.myCommandType, other.myArgs, other.myArgsLeadingSpaces, other.myAddEOL, other.myCommented, other.mySuppressibleCommented, other.myAddBeforeClosing); + this(other.myCommandType, other.myArgs, other.myAddEOL, other.myCommented, other.mySuppressibleCommented, other.myAddBeforeClosing); } @Override diff --git a/src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CMakeCommandBase.java b/src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CMakeCommandBase.java index fb146f3..8fb9a7a 100644 --- a/src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CMakeCommandBase.java +++ b/src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CMakeCommandBase.java @@ -9,45 +9,43 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; public abstract class CMakeCommandBase implements CMakeElement { final protected @NotNull CMakeCommandType myCommandType; - final protected @NotNull ArrayList myArgs; - final protected @NotNull ArrayList myArgsLeadingSpaces; + final protected @NotNull ArrayList myArgs; protected boolean myAddEOL; protected boolean myCommented; protected boolean mySuppressibleCommented; - protected BasedSequence myLeadingSpaces = BasedSequence.NULL; - protected BasedSequence myAddBeforeClosing = BasedSequence.NULL; + protected BasedSequence myLeadingSpaces; + protected BasedSequence myAddBeforeClosing; - public CMakeCommandBase(@NotNull final CMakeCommandType commandType, @NotNull final List args, @Nullable List argsLeadingSpaces, boolean isAddEOL, boolean commented, boolean suppressibleCommented, @NotNull BasedSequence addBeforeClosing) { + public CMakeCommandBase(@NotNull final CMakeCommandType commandType, @NotNull final List args, boolean isAddEOL, boolean commented, boolean suppressibleCommented, @NotNull BasedSequence addBeforeClosing) { myCommandType = commandType; myAddEOL = isAddEOL; myCommented = commented; mySuppressibleCommented = suppressibleCommented; myAddBeforeClosing = addBeforeClosing; + myLeadingSpaces = BasedSequence.NULL; myArgs = new ArrayList<>(args); - myArgsLeadingSpaces = argsLeadingSpaces == null ? new ArrayList<>() : new ArrayList<>(argsLeadingSpaces); - if (argsLeadingSpaces == null) { - int iMax = args.size(); - for (int i = 0; i < iMax; i++) { - myArgsLeadingSpaces.add(BasedSequence.NULL); - } - } - + String[] defaults = commandType.getDefaultArgs(); - int iMax = defaults.length; // set defaults for missing or null values + int iMax = defaults.length; for (int i = 0; i < iMax; i++) { if (i >= myArgs.size() || myArgs.get(i) == null) { - addArg(defaults[i], BasedSequence.NULL); + setOrAddArg(i, CommandArgument.of(defaults[i])); + } + } + + iMax = myArgs.size(); + for (int i = 0; i < iMax; i++) { + if (myArgs.get(i) == null) { + myArgs.set(i, CommandArgument.NULL); } } } @@ -145,10 +143,11 @@ public void appendTo(StringBuilder out, final @Nullable String projectNameMacro, if (pos == -1) break; if (wildcardArg >= myArgs.size()) break; - String replacement = myArgs.get(wildcardArg); - argLeadingSpaces = myArgsLeadingSpaces.get(wildcardArg); + CommandArgument cmdArg = myArgs.get(wildcardArg); + String replacement = cmdArg.getValue().toString(); + argLeadingSpaces = cmdArg.getLeadingSpaces(); - if (wildcardArg < myCommandType.myDefaultArgs.length && myCommandType.myDefaultArgs[wildcardArg].equals(replacement)) { + if (wildcardArg < myCommandType.myDefaultArgs.length && replacement.equals(myCommandType.myDefaultArgs[wildcardArg])) { // its a default, we replace values in it replacement = CMakeListsTxtBuilder.Companion.replacedCommandParams(replacement, valueSet); } @@ -183,13 +182,12 @@ public void appendTo(StringBuilder out, final @Nullable String projectNameMacro, } int iMax = myArgs.size(); - + for (int i = wildcardArg; i < iMax; i++) { - String arg = myArgs.get(i); + CommandArgument cmdArg = myArgs.get(i); + String arg = cmdArg.getValue().toString(); if (arg.isEmpty()) continue; - BasedSequence argLeadingSpaces = myArgsLeadingSpaces.get(i); - if (i < myCommandType.myDefaultArgs.length && myCommandType.myDefaultArgs[i].equals(arg)) { // its a default, we replace values in it arg = CMakeListsTxtBuilder.Companion.replacedCommandParams(arg, valueSet); @@ -198,10 +196,10 @@ public void appendTo(StringBuilder out, final @Nullable String projectNameMacro, if (!myCommandType.isNoDupeArgs() || !argValues.contains(arg)) { argValues.add(arg); - if (!argLeadingSpaces.isEmpty()) out.append(argLeadingSpaces); + if (cmdArg.getLeadingSpaces().isNotEmpty()) out.append(cmdArg.getLeadingSpaces()); else out.append(sep); sep = " "; - + out.append(CMakeParser.getArgText(arg)); } } @@ -224,97 +222,82 @@ public boolean isOfType(CMakeCommandType commandType) { return myCommandType.isOfType(commandType); } - public List getArgs() { + public @NotNull List getArgs() { return myArgs; } - public List getArgsLeadingSpaces() { - return myArgsLeadingSpaces; - } - // get leading spaces for matching args, BasedSequence.NULL for the rest - public List getArgsLeadingSpaces(Collection args) { - return getArgsLeadingSpaces(args, 0, args.size()); + public List getArgsWithLeadingSpaces(Collection args) { + return getArgsWithLeadingSpaces(args, 0, args.size()); } - + // get leading spaces for matching args, BasedSequence.NULL for the rest - public List getArgsLeadingSpaces(Collection args, int start, int end) { - ArrayList argsLeadingSpaces = new ArrayList<>(); - + public List getArgsWithLeadingSpaces(Collection args, int start, int end) { + ArrayList argsWithLeadingSpaces = new ArrayList<>(); + int i = 0; - for (String arg : args) { - if (i >= end) return argsLeadingSpaces; + for (CommandArgument arg : args) { + if (i >= end) return argsWithLeadingSpaces; if (i >= start) { + CommandArgument cmdArg = i < myArgs.size() ? myArgs.get(i) : CommandArgument.NULL; + if (arg != null) { - if (i < myArgs.size()) { - if (!arg.equals(myArgs.get(i))) { - argsLeadingSpaces.add(BasedSequence.NULL); + if (!cmdArg.isNull()) { + if (!cmdArg.isEqualValue(arg)) { + argsWithLeadingSpaces.add(arg.withLeadingSpaces(BasedSequence.NULL)); } else { - argsLeadingSpaces.add(myArgsLeadingSpaces.get(i)); + argsWithLeadingSpaces.add(arg.withLeadingSpaces(cmdArg.getLeadingSpaces())); } } else { - argsLeadingSpaces.add(BasedSequence.NULL); + argsWithLeadingSpaces.add(arg.withLeadingSpaces(BasedSequence.NULL)); } } else { - if (i >= myArgs.size() || !myArgs.get(i).isEmpty()) { - argsLeadingSpaces.add(BasedSequence.NULL); + if (cmdArg.isEmpty()) { + argsWithLeadingSpaces.add(CommandArgument.NULL); } else { - argsLeadingSpaces.add(myArgsLeadingSpaces.get(i)); + argsWithLeadingSpaces.add(cmdArg.withValue(null)); } } } i++; } - return argsLeadingSpaces; + return argsWithLeadingSpaces; } public int getArgCount() { return myArgs.size(); } - public @NotNull String getArg(int index) { + public @NotNull CommandArgument getArg(int index) { return myArgs.get(index); } - public @NotNull BasedSequence getArgLeadingSpaces(int index) { - return myArgsLeadingSpaces.get(index); - } - - public @NotNull String arg(int index) { - return index < myArgs.size() ? myArgs.get(index) : ""; - } - - public @NotNull BasedSequence argLeadingSpaces(int index) { - return index < myArgsLeadingSpaces.size() ? myArgsLeadingSpaces.get(index) : BasedSequence.NULL; + public @NotNull CommandArgument arg(int index) { + return index < myArgs.size() ? myArgs.get(index) : CommandArgument.NULL; } public void extendArgs(int index) { while (index >= myArgs.size()) { - myArgs.add(""); - myArgsLeadingSpaces.add(BasedSequence.NULL); + myArgs.add(CommandArgument.NULL); } } - public void setArgsWithDefaults(@NotNull Collection args, @Nullable Collection argsLeadingSpaces) { + public void setArgsWithDefaults(@NotNull Collection args) { myArgs.clear(); int i = 0; int j = 0; // default args String[] defaultArgs = myCommandType.getDefaultArgs(); - Iterator leadingSpacesIterator = argsLeadingSpaces == null ? null : argsLeadingSpaces.iterator(); - for (String arg : args) { - if (arg != null) { + for (CommandArgument arg : args) { + if (arg.isNotNull()) { extendArgs(i); myArgs.set(i, arg); - BasedSequence leadingSpaces = leadingSpacesIterator == null /*|| !leadingSpacesIterator.hasNext()*/ ? BasedSequence.NULL : leadingSpacesIterator.next(); - myArgsLeadingSpaces.set(i, leadingSpaces == null ? BasedSequence.NULL : leadingSpaces); } else { if (j < defaultArgs.length) { extendArgs(i); - myArgs.set(i, defaultArgs[j]); - myArgsLeadingSpaces.set(i, BasedSequence.NULL); + myArgs.set(i, CommandArgument.of(defaultArgs[j])); } } j++; @@ -323,26 +306,25 @@ public void setArgsWithDefaults(@NotNull Collection args, @Nullable Coll while (j < defaultArgs.length) { extendArgs(i); - myArgs.set(i, defaultArgs[j]); - myArgsLeadingSpaces.set(i, BasedSequence.NULL); + myArgs.set(i, CommandArgument.of(defaultArgs[j])); j++; i++; } } - public boolean allArgsEqual(final Collection args) { + public boolean allArgsEqual(final Collection args) { return allArgsEqual(args, 0, args.size()); } - public boolean allArgsEqual(final Collection args, int start, int end) { + public boolean allArgsEqual(final Collection args, int start, int end) { int i = 0; - for (String arg : args) { + for (CommandArgument arg : args) { if (i >= end) return true; if (i >= start) { if (arg != null) { if (i < myArgs.size()) { - if (!arg.equals(myArgs.get(i))) { + if (!myArgs.get(i).isEqualValue(arg)) { return false; } } else { @@ -364,43 +346,30 @@ public boolean allArgsEqual(final Collection args, int start, int end) { // clear to defaults public void clearToDefaults() { myArgs.clear(); - myArgsLeadingSpaces.clear(); - - Collections.addAll(myArgs, myCommandType.myDefaultArgs); for (String arg : myCommandType.myDefaultArgs) { - myArgsLeadingSpaces.add(BasedSequence.NULL); + myArgs.add(CommandArgument.of(arg)); } } // raw manipulation - public void setArg(int index, @NotNull String arg, @NotNull BasedSequence leadingSpaces) { + public void setOrAddArg(int index, @NotNull CommandArgument arg) { if (index == getArgCount()) { myArgs.add(arg); - myArgsLeadingSpaces.add(leadingSpaces); } else { myArgs.set(index, arg); - myArgsLeadingSpaces.set(index, leadingSpaces); } } public void clearArgs() { myArgs.clear(); - myArgsLeadingSpaces.clear(); } - public void addArg(@NotNull String arg, @NotNull BasedSequence argLeadingSpaces) { + public void addArg(@NotNull CommandArgument arg) { myArgs.add(arg); - myArgsLeadingSpaces.add(argLeadingSpaces); - } - - public void addArg(int index, @NotNull String arg, @NotNull BasedSequence argLeadingSpaces) { - myArgs.set(index, arg); - myArgsLeadingSpaces.set(index, argLeadingSpaces); } public void removeArg(int index) { myArgs.remove(index); - myArgsLeadingSpaces.remove(index); } } diff --git a/src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CMakeUnknownCommand.java b/src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CMakeUnknownCommand.java index 120a9c8..9674c6a 100644 --- a/src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CMakeUnknownCommand.java +++ b/src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CMakeUnknownCommand.java @@ -10,21 +10,21 @@ public class CMakeUnknownCommand extends CMakeCommandBase { final protected @NotNull String myCommandName; - public CMakeUnknownCommand(@NotNull final String commandName, @NotNull final List args, @Nullable List argsLeadingSpaces, boolean isAddEOL, boolean commented, boolean suppressibleCommented, @NotNull BasedSequence addEolBeforeClosing) { - super(CMakeCommandType.UNKNOWN, args, argsLeadingSpaces, isAddEOL, commented, suppressibleCommented, addEolBeforeClosing); + public CMakeUnknownCommand(@NotNull final String commandName, @NotNull final List args, boolean isAddEOL, boolean commented, boolean suppressibleCommented, @NotNull BasedSequence addEolBeforeClosing) { + super(CMakeCommandType.UNKNOWN, args, isAddEOL, commented, suppressibleCommented, addEolBeforeClosing); myCommandName = commandName; } - public CMakeUnknownCommand(@NotNull final String commandName, @NotNull final List args, @Nullable List argsLeadingSpaces, boolean isAddEOL, @NotNull BasedSequence addEolBeforeClosing) { - this(commandName, args, argsLeadingSpaces, isAddEOL, false, false, addEolBeforeClosing); + public CMakeUnknownCommand(@NotNull final String commandName, @NotNull final List args, boolean isAddEOL, @NotNull BasedSequence addEolBeforeClosing) { + this(commandName, args, isAddEOL, false, false, addEolBeforeClosing); } public CMakeUnknownCommand(@NotNull final String commandName, boolean isAddEOL, @NotNull BasedSequence addEolBeforeClosing) { - this(commandName, Collections.emptyList(), Collections.emptyList(), isAddEOL, false, false, addEolBeforeClosing); + this(commandName, Collections.emptyList(), isAddEOL, false, false, addEolBeforeClosing); } public CMakeUnknownCommand(@NotNull final CMakeUnknownCommand other) { - this(other.myCommandName, other.myArgs, other.myArgsLeadingSpaces, other.myAddEOL, other.myCommented, other.mySuppressibleCommented, other.myAddBeforeClosing); + this(other.myCommandName, other.myArgs, other.myAddEOL, other.myCommented, other.mySuppressibleCommented, other.myAddBeforeClosing); } @Override diff --git a/src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CommandArgument.java b/src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CommandArgument.java new file mode 100644 index 0000000..5c87c35 --- /dev/null +++ b/src/com/vladsch/clionarduinoplugin/generators/cmake/commands/CommandArgument.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2016-2023 Vladimir Schneider + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.vladsch.clionarduinoplugin.generators.cmake.commands; + +import com.vladsch.flexmark.util.sequence.BasedSequence; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +public class CommandArgument { + final protected @NotNull BasedSequence myValue; + final protected @NotNull BasedSequence myLeadingSpaces; + + protected CommandArgument(@NotNull BasedSequence value, @NotNull BasedSequence leadingSpaces) { + myValue = value; + myLeadingSpaces = leadingSpaces; + } + + public @NotNull BasedSequence getValue() { + return myValue; + } + + public @NotNull BasedSequence getLeadingSpaces() { + return myLeadingSpaces; + } + + public boolean isEqualValue(@NotNull CommandArgument arg) { + return Objects.equals(arg.myValue, myValue); + } + + public boolean isEqualValue(@NotNull BasedSequence value) { + return Objects.equals(value, myValue); + } + + public boolean isEqualValue(@Nullable String value) { + return isEqualValue(BasedSequence.of(value)); + } + + public boolean isEmpty() { + return myValue.isEmpty(); + } + + public boolean isNull() { + return this == NULL; + } + + public boolean isNotNull() { + return this != NULL; + } + + public CommandArgument withValue(@Nullable String value) { + return copyOf(this, value, myLeadingSpaces); + } + + public CommandArgument withLeadingSpaces(@NotNull BasedSequence leadingSpaces) { + return copyOf(this, myValue, leadingSpaces); + } + + final public static CommandArgument NULL = new CommandArgument(BasedSequence.NULL, BasedSequence.NULL); + + public static CommandArgument of(@NotNull BasedSequence arg, @NotNull BasedSequence leadingSpaces) { + return new CommandArgument(arg, leadingSpaces); + } + + public static CommandArgument copyOf(@NotNull CommandArgument other, @NotNull BasedSequence arg, @NotNull BasedSequence leadingSpaces) { + return other.isEqualValue(arg) && Objects.equals(other.myLeadingSpaces, leadingSpaces) ? other : new CommandArgument(arg, leadingSpaces); + } + + public static CommandArgument copyOf(@NotNull CommandArgument other, @Nullable String arg, @NotNull BasedSequence leadingSpaces) { + return copyOf(other, BasedSequence.of(arg), leadingSpaces); + } + + public static CommandArgument of(@NotNull BasedSequence arg) { + return new CommandArgument(arg, BasedSequence.NULL); + } + + public static CommandArgument of(@Nullable String arg, @NotNull BasedSequence leadingSpaces) { + return of(BasedSequence.of(arg), leadingSpaces); + } + + public static CommandArgument of(@Nullable String arg) { + return of(BasedSequence.of(arg)); + } +} diff --git a/test/com/vladsch/clionarduinoplugin/generators/cmake/ArduinoCMakeBuilderTest.kt b/test/com/vladsch/clionarduinoplugin/generators/cmake/ArduinoCMakeBuilderTest.kt index 7a6fc71..4757007 100644 --- a/test/com/vladsch/clionarduinoplugin/generators/cmake/ArduinoCMakeBuilderTest.kt +++ b/test/com/vladsch/clionarduinoplugin/generators/cmake/ArduinoCMakeBuilderTest.kt @@ -109,7 +109,7 @@ set(lib4_RECURSE false) assertEquals("Argument count differs", exp.size, act.argCount) for (j in 0..exp.size - 1) { - assertEquals("Item[$i+1] argument ${j + 1} differs", exp[j], actual[i].getArg(j)) + assertEquals("Item[$i+1] argument ${j + 1} differs", exp[j], actual[i].getArg(j).value.toString()) } } }