fun parseOptionLine()

in vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/model/commands/SetCommand.kt [103:220]


fun parseOptionLine(
  editor: VimEditor,
  context: ExecutionContext,
  commandModifier: CommandModifier,
  argument: String,
  scope: OptionAccessScope,
) {
  val optionGroup = injector.optionGroup

  val columnFormat = commandModifier == CommandModifier.BANG

  when {
    argument.isEmpty() -> {
      // No arguments mean we show only changed values
      val changedOptions = optionGroup.getAllOptions()
        .filter {
          !optionGroup.isDefaultValue(
            it,
            scope
          ) && (!it.isHidden || (injector.application.isInternal() && !injector.application.isUnitTest()))
        }
        .map { Pair(it.name, it.name) }
      showOptions(editor, context, changedOptions, scope, true, columnFormat)
      return
    }

    argument == "all" -> {
      val options = optionGroup.getAllOptions()
        .filter { !it.isHidden || (injector.application.isInternal() && !injector.application.isUnitTest()) }
        .map { Pair(it.name, it.name) }
      showOptions(editor, context, options, scope, true, columnFormat)
      return
    }

    argument == "all&" -> {
      // Note that `all&` resets all options in the current editor at local and global scope. This includes global,
      // global-local and local-to-buffer options, which will affect other windows. It does not affect the local values
      // of local-to-window options in other windows
      optionGroup.resetAllOptions(editor)
      return
    }
  }

  // We now have 1 or more option operators separator by spaces
  var error: String? = null
  val tokenizer = StringTokenizer(argument)
  val toShow = mutableListOf<Pair<String, String>>()
  while (tokenizer.hasMoreTokens()) {
    var token = tokenizer.nextToken()
    // See if a space has been backslashed, if not get the rest of the text
    while (token.endsWith("\\")) {
      token = token.take(token.length - 1) + ' '
      if (tokenizer.hasMoreTokens()) {
        token += tokenizer.nextToken()
      }
    }

    val eq = token.indexOf('=')
    val colon = token.indexOf(':')
    val isKeyValueOperation = eq != -1 || colon != -1
    if (!isKeyValueOperation) {
      when {
        token.endsWith("?") -> toShow.add(Pair(token.dropLast(1), token))
        token.startsWith("no") -> optionGroup.unsetToggleOption(getValidToggleOption(token.substring(2), token), scope)
        token.startsWith("inv") -> optionGroup.invertToggleOption(
          getValidToggleOption(token.substring(3), token),
          scope
        )

        token.endsWith("!") -> optionGroup.invertToggleOption(getValidToggleOption(token.dropLast(1), token), scope)
        token.endsWith("&") -> optionGroup.resetToDefaultValue(getValidOption(token.dropLast(1), token), scope)
        token.endsWith("<") -> optionGroup.resetToGlobalValue(getValidOption(token.dropLast(1), token), scope, editor)
        else -> {
          // `getOption` returns `Option<VimDataType>?`, but we need to treat it as `Option<out VimDataType>?` because
          // `ToggleOption` derives from `Option<out VimDataType>`, and the compiler will complain if the types are
          // different.
          val option: Option<out VimDataType>? = optionGroup.getOption(token)
          when (option) {
            null -> error = injector.messages.message("E518", token)
            is ToggleOption -> optionGroup.setToggleOption(option, scope)
            else -> toShow.add(Pair(option.name, option.abbrev))
          }
        }
      }
    } else {
      // This must be one of =, :, +=, -=, or ^=
      // Could be option:value, option=value, option+=value, option-=value or option^=value
      val idx = if (eq > 0) eq else colon
      val op = if (eq > 0) token[eq - 1] else Char(0)
      val end = if (eq > 0 && op in "+-^") idx - 1 else idx

      // Get option name and value after operator
      val optionName = token.take(end)
      val option = getValidOption(optionName)
      val existingValue = optionGroup.getOptionValue(option, scope)
      val value = option.parseValue(token.substring(idx + 1), token)
      val newValue = when (op) {
        '+' -> appendValue(option, existingValue, value)
        '^' -> prependValue(option, existingValue, value)
        '-' -> removeValue(option, existingValue, value)
        else -> value
      } ?: throw exExceptionMessage("E474.arg", token)
      optionGroup.setOptionValue(option, scope, newValue)
    }
    if (error != null) {
      break
    }
  }

  // Now show all options that were individually requested
  if (toShow.isNotEmpty()) {
    showOptions(editor, context, toShow, scope, false, columnFormat)
  }

  if (error != null) {
    throw ExException(error)
  }
}