docs: def add(self, name, value, showkey=None, before=None,
              preserve_spacing=True):
          """Add a parameter to the template with a given *name* and *value*.
  
          *name* and *value* can be anything parsable by
          :func:`.utils.parse_anything`; pipes and equal signs are automatically
          escaped from *value* when appropriate.
  
          If *name* is already a parameter in the template, we'll replace its
          value.
  
          If *showkey* is given, this will determine whether or not to show the
          parameter's name (e.g., ``{{foo|bar}}``'s parameter has a name of
          ``"1"`` but it is hidden); otherwise, we'll make a safe and intelligent
          guess.
  
          If *before* is given (either a :class:`.Parameter` object or a name),
          then we will place the parameter immediately before this one.
          Otherwise, it will be added at the end. If *before* is a name and
          exists multiple times in the template, we will place it before the last
          occurrence. If *before* is not in the template, :exc:`ValueError` is
          raised. The argument is ignored if *name* is an existing parameter.
  
          If *preserve_spacing* is ``True``, we will try to preserve whitespace
          conventions around the parameter, whether it is new or we are updating
          an existing value. It is disabled for parameters with hidden keys,
          since MediaWiki doesn't strip whitespace in this case.
          """
          name, value = parse_anything(name), parse_anything(value)
          self._surface_escape(value, "|")
  
          if self.has(name):
              self.remove(name, keep_field=True)
              existing = self.get(name)
              if showkey is not None:
                  existing.showkey = showkey
              if not existing.showkey:
                  self._surface_escape(value, "=")
              nodes = existing.value.nodes
              if preserve_spacing and existing.showkey:
                  for i in range(2):  # Ignore empty text nodes
                      if not nodes[i]:
                          nodes[i] = None
                  existing.value = parse_anything([nodes[0], value, nodes[1]])
              else:
                  existing.value = value
              return existing
  
          if showkey is None:
              if Parameter.can_hide_key(name):
                  int_name = int(str(name))
                  int_keys = set()
                  for param in self.params:
                      if not param.showkey:
                          int_keys.add(int(str(param.name)))
                  expected = min(set(range(1, len(int_keys) + 2)) - int_keys)
                  if expected == int_name:
                      showkey = False
                  else:
                      showkey = True
              else:
                  showkey = True
          if not showkey:
              self._surface_escape(value, "=")
  
          if preserve_spacing and showkey:
              before_n, after_n = self._get_spacing_conventions(use_names=True)
              before_v, after_v = self._get_spacing_conventions(use_names=False)
              name = parse_anything([before_n, name, after_n])
              value = parse_anything([before_v, value, after_v])
  
          param = Parameter(name, value, showkey)
          if before:
              if not isinstance(before, Parameter):
                  before = self.get(before)
              self.params.insert(self.params.index(before), param)
          else:
              self.params.append(param)
          return param