Exemplo n.º 1
0
/// Set a value in a dict. Objects are recursively expanded into their
/// vimscript equivalents. Passing 'nil' as value deletes the key.
///
/// @param dict The vimscript dict
/// @param key The key
/// @param value The new value
/// @param[out] err Details of an error that may have occurred
/// @return the old value, if any
Object dict_set_value(dict_T *dict, String key, Object value, Error *err)
{
  Object rv = OBJECT_INIT;

  if (dict->dv_lock) {
    set_api_error("Dictionary is locked", err);
    return rv;
  }

  if (key.size == 0) {
    set_api_error("Empty dictionary keys aren't allowed", err);
    return rv;
  }

  if (key.size > INT_MAX) {
    set_api_error("Key length is too high", err);
    return rv;
  }

  dictitem_T *di = dict_find(dict, (uint8_t *)key.data, (int)key.size);

  if (value.type == kObjectTypeNil) {
    // Delete the key
    if (di == NULL) {
      // Doesn't exist, fail
      set_api_error("Key doesn't exist", err);
    } else {
      // Return the old value
      rv = vim_to_object(&di->di_tv);
      // Delete the entry
      hashitem_T *hi = hash_find(&dict->dv_hashtab, di->di_key);
      hash_remove(&dict->dv_hashtab, hi);
      dictitem_free(di);
    }
  } else {
    // Update the key
    typval_T tv;

    // Convert the object to a vimscript type in the temporary variable
    if (!object_to_vim(value, &tv, err)) {
      return rv;
    }

    if (di == NULL) {
      // Need to create an entry
      di = dictitem_alloc((uint8_t *) key.data);
      dict_add(dict, di);
    } else {
      // Return the old value
      clear_tv(&di->di_tv);
    }

    // Update the value
    copy_tv(&tv, &di->di_tv);
    // Clear the temporary variable
    clear_tv(&tv);
  }

  return rv;
}
Exemplo n.º 2
0
/// Calculates the number of display cells `str` occupies, tab is counted as
/// one cell.
///
/// @param str Some text
/// @param[out] err Details of an error that may have occurred
/// @return The number of cells
Integer vim_strwidth(String str, Error *err)
{
  if (str.size > INT_MAX) {
    set_api_error("String length is too high", err);
    return 0;
  }

  return (Integer) mb_string2cells((char_u *) str.data);
}
Exemplo n.º 3
0
/// Finds the pointer for a window number
///
/// @param window the window number
/// @param[out] err Details of an error that may have occurred
/// @return the window pointer
win_T * find_window(Window window, Error *err)
{
  win_T *rv = handle_get_window(window);

  if (!rv) {
    set_api_error("Invalid window id", err);
  }

  return rv;
}
Exemplo n.º 4
0
/// Finds the pointer for a tabpage number
///
/// @param tabpage the tabpage number
/// @param[out] err Details of an error that may have occurred
/// @return the tabpage pointer
tabpage_T * find_tab(Tabpage tabpage, Error *err)
{
  tabpage_T *rv = handle_get_tabpage(tabpage);

  if (!rv) {
    set_api_error("Invalid tabpage id", err);
  }

  return rv;
}
Exemplo n.º 5
0
/// Finds the pointer for a window number
///
/// @param window the window number
/// @param[out] err Details of an error that may have occurred
/// @return the window pointer
buf_T *find_buffer(Buffer buffer, Error *err)
{
  buf_T *rv = handle_get_buffer(buffer);

  if (!rv) {
    set_api_error("Invalid buffer id", err);
  }

  return rv;
}
Exemplo n.º 6
0
Arquivo: vim.c Projeto: Saneyan/neovim
Integer vim_strwidth(String str, Error *err)
{
  if (str.size > INT_MAX) {
    set_api_error("String length is too high", err);
    return 0;
  }

  char *buf = xstrndup(str.data, str.size);
  Integer rv = mb_string2cells((char_u *)buf, -1);
  free(buf);
  return rv;
}
Exemplo n.º 7
0
/// Recursively expands a vimscript value in a dict
///
/// @param dict The vimscript dict
/// @param key The key
/// @param[out] err Details of an error that may have occurred
Object dict_get_value(dict_T *dict, String key, Error *err)
{
  hashitem_T *hi = hash_find(&dict->dv_hashtab, (uint8_t *) key.data);

  if (HASHITEM_EMPTY(hi)) {
    set_api_error("Key not found", err);
    return (Object) OBJECT_INIT;
  }

  dictitem_T *di = dict_lookup(hi);
  return vim_to_object(&di->di_tv);
}
Exemplo n.º 8
0
/// Gets the value of a global or local(buffer, window) option.
///
/// @param from If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer
///        to the window or buffer.
/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF`
/// @param name The option name
/// @param[out] err Details of an error that may have occurred
/// @return the option value
Object get_option_from(void *from, int type, String name, Error *err)
{
  Object rv = OBJECT_INIT;

  if (name.size == 0) {
    set_api_error("Empty option name", err);
    return rv;
  }

  // Return values
  int64_t numval;
  char *stringval = NULL;
  int flags = get_option_value_strict(name.data, &numval, &stringval,
                                      type, from);

  if (!flags) {
    set_api_error("invalid option name", err);
    return rv;
  }

  if (flags & SOPT_BOOL) {
    rv.type = kObjectTypeBoolean;
    rv.data.boolean = numval ? true : false;
  } else if (flags & SOPT_NUM) {
    rv.type = kObjectTypeInteger;
    rv.data.integer = numval;
  } else if (flags & SOPT_STRING) {
    if (stringval) {
      rv.type = kObjectTypeString;
      rv.data.string.data = stringval;
      rv.data.string.size = strlen(stringval);
    } else {
      set_api_error(N_("Unable to get option value"), err);
    }
  } else {
    set_api_error(N_("internal error: unknown option type"), err);
  }

  return rv;
}
Exemplo n.º 9
0
/// Changes vim working directory
///
/// @param dir The new working directory
/// @param[out] err Details of an error that may have occurred
void vim_change_directory(String dir, Error *err)
{
  if (dir.size >= MAXPATHL) {
    set_api_error("directory string is too long", err);
    return;
  }

  char string[MAXPATHL];
  strncpy(string, dir.data, dir.size);
  string[dir.size] = NUL;

  try_start();

  if (vim_chdir((char_u *)string)) {
    if (!try_end(err)) {
      set_api_error("failed to change directory", err);
    }
    return;
  }

  post_chdir(false);
  try_end(err);
}
Exemplo n.º 10
0
bool try_end(Error *err)
{
  --trylevel;

  // Without this it stops processing all subsequent VimL commands and
  // generates strange error messages if I e.g. try calling Test() in a
  // cycle
  did_emsg = false;

  if (got_int) {
    if (did_throw) {
      // If we got an interrupt, discard the current exception
      discard_current_exception();
    }

    set_api_error("Keyboard interrupt", err);
    got_int = false;
  } else if (msg_list != NULL && *msg_list != NULL) {
    int should_free;
    char *msg = (char *)get_exception_string(*msg_list,
                                             ET_ERROR,
                                             NULL,
                                             &should_free);
    strncpy(err->msg, msg, sizeof(err->msg));
    err->set = true;
    free_global_msglist();

    if (should_free) {
      free(msg);
    }
  } else if (did_throw) {
    set_api_error((char *)current_exception->value, err);
  }

  return err->set;
}
Exemplo n.º 11
0
/// Pass input keys to Neovim
///
/// @param keys to be typed
/// @param replace_tcodes If true replace special keys such as <CR> or <Leader>
///           for compatibility with Vim --remote-send expressions
/// @param remap If True remap keys
/// @param typed Handle keys as if typed; otherwise they are handled as
///           if coming from a mapping.  This matters for undo,
///           opening folds, etc.
void vim_feedkeys(String keys, Boolean replace_tcodes, Boolean remap,
                Boolean typed, Error *err)
{
  char *ptr = NULL;
  char *cpo_save = (char *)p_cpo;

  if (replace_tcodes) {
    // Set 'cpoptions' the way we want it.
    //    B set - backslashes are *not* treated specially
    //    k set - keycodes are *not* reverse-engineered
    //    < unset - <Key> sequences *are* interpreted
    //  The last but one parameter of replace_termcodes() is TRUE so that the
    //  <lt> sequence is recognised - needed for a real backslash.
    p_cpo = (char_u *)"Bk";
    replace_termcodes((char_u *)keys.data, (char_u **)&ptr, false, true, true);
    p_cpo = (char_u *)cpo_save;
  } else {
    ptr = keys.data;
  }

  if (ptr == NULL) {
    set_api_error("Failed to eval expression", err);
  } else {
    // Add the string to the input stream.
    // Can't use add_to_input_buf() here, we now have K_SPECIAL bytes.
    //
    // First clear typed characters from the typeahead buffer, there could
    // be half a mapping there.  Then append to the existing string, so
    // that multiple commands from a client are concatenated.
    if (typebuf.tb_maplen < typebuf.tb_len) {
        del_typebuf(typebuf.tb_len - typebuf.tb_maplen, typebuf.tb_maplen);
    }
    (void)ins_typebuf((char_u *)ptr, (remap ? REMAP_YES : REMAP_NONE),
                    typebuf.tb_len, !typed, false);

    // Let input_available() know we inserted text in the typeahead
    // buffer. */
    typebuf_was_filled = true;

    if (replace_tcodes) {
      free(ptr);
    }
  }
}
Exemplo n.º 12
0
Object dict_get_value(dict_T *dict, String key, Error *err)
{
  Object rv;
  hashitem_T *hi;
  dictitem_T *di;
  char *k = xstrndup(key.data, key.size);
  hi = hash_find(&dict->dv_hashtab, (uint8_t *)k);
  free(k);

  if (HASHITEM_EMPTY(hi)) {
    set_api_error("Key not found", err);
    return rv;
  }

  di = dict_lookup(hi);
  rv = vim_to_object(&di->di_tv);

  return rv;
}
Exemplo n.º 13
0
/// Evaluates the expression str using the vim internal expression
/// evaluator (see |expression|).
/// Dictionaries and lists are recursively expanded.
///
/// @param str The expression str
/// @param[out] err Details of an error that may have occurred
/// @return The expanded object
Object vim_eval(String str, Error *err)
{
  Object rv;
  // Evaluate the expression
  try_start();
  typval_T *expr_result = eval_expr((char_u *) str.data, NULL);

  if (!expr_result) {
    set_api_error("Failed to eval expression", err);
  }

  if (!try_end(err)) {
    // No errors, convert the result
    rv = vim_to_object(expr_result);
  }

  // Free the vim object
  free_tv(expr_result);
  return rv;
}
Exemplo n.º 14
0
/// Sets the current window
///
/// @param handle The window handle
void vim_set_current_window(Window window, Error *err)
{
  win_T *win = find_window_by_handle(window, err);

  if (!win) {
    return;
  }

  try_start();
  goto_tabpage_win(win_find_tabpage(win), win);

  if (win != curwin) {
    if (try_end(err)) {
      return;
    }
    set_api_error("did not switch to the specified window", err);
    return;
  }

  try_end(err);
}
Exemplo n.º 15
0
/// Sets the current buffer
///
/// @param id The buffer handle
/// @param[out] err Details of an error that may have occurred
void vim_set_current_buffer(Buffer buffer, Error *err)
{
  buf_T *buf = find_buffer_by_handle(buffer, err);

  if (!buf) {
    return;
  }

  try_start();
  if (do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0) == FAIL) {
    if (try_end(err)) {
      return;
    }

    char msg[256];
    snprintf(msg, sizeof(msg), "failed to switch to buffer %d", (int)buffer);
    set_api_error(msg, err);
    return;
  }

  try_end(err);
}
Exemplo n.º 16
0
/// Sets the value of a global or local(buffer, window) option.
///
/// @param to If `type` is `SREQ_WIN` or `SREQ_BUF`, this must be a pointer
///        to the window or buffer.
/// @param type One of `SREQ_GLOBAL`, `SREQ_WIN` or `SREQ_BUF`
/// @param name The option name
/// @param[out] err Details of an error that may have occurred
void set_option_to(void *to, int type, String name, Object value, Error *err)
{
  if (name.size == 0) {
    set_api_error("Empty option name", err);
    return;
  }

  int flags = get_option_value_strict(name.data, NULL, NULL, type, to);

  if (flags == 0) {
    set_api_error("invalid option name", err);
    return;
  }

  if (value.type == kObjectTypeNil) {
    if (type == SREQ_GLOBAL) {
      set_api_error("unable to unset option", err);
      return;
    } else if (!(flags & SOPT_GLOBAL)) {
      set_api_error("cannot unset option that doesn't have a global value",
                     err);
      return;
    } else {
      unset_global_local_option(name.data, to);
      return;
    }
  }

  int opt_flags = (type ? OPT_LOCAL : OPT_GLOBAL);

  if (flags & SOPT_BOOL) {
    if (value.type != kObjectTypeBoolean) {
      set_api_error("option requires a boolean value", err);
      return;
    }

    bool val = value.data.boolean;
    set_option_value_for(name.data, val, NULL, opt_flags, type, to, err);
  } else if (flags & SOPT_NUM) {
    if (value.type != kObjectTypeInteger) {
      set_api_error("option requires an integer value", err);
      return;
    }

    if (value.data.integer > INT_MAX || value.data.integer < INT_MIN) {
      set_api_error("Option value outside range", err);
      return;
    }

    int val = (int) value.data.integer;
    set_option_value_for(name.data, val, NULL, opt_flags, type, to, err);
  } else {
    if (value.type != kObjectTypeString) {
      set_api_error("option requires a string value", err);
      return;
    }

    set_option_value_for(name.data, 0, value.data.string.data,
            opt_flags, type, to, err);
  }
}
Exemplo n.º 17
0
/// Copies a C string into a String (binary safe string, characters + length).
/// The resulting string is also NUL-terminated, to facilitate interoperating
/// with code using C strings.
///
/// @param str the C string to copy
/// @return the resulting String, if the input string was NULL, an
///         empty String is returned
String cstr_to_string(const char *str)
{
    if (str == NULL) {
        return (String) STRING_INIT;
    }

    size_t len = strlen(str);
    return (String) {
        .data = xmemdupz(str, len),
        .size = len
    };
}

static bool object_to_vim(Object obj, typval_T *tv, Error *err)
{
  tv->v_type = VAR_UNKNOWN;
  tv->v_lock = 0;

  switch (obj.type) {
    case kObjectTypeNil:
      tv->v_type = VAR_NUMBER;
      tv->vval.v_number = 0;
      break;

    case kObjectTypeBoolean:
      tv->v_type = VAR_NUMBER;
      tv->vval.v_number = obj.data.boolean;
      break;

    case kObjectTypeInteger:
      if (obj.data.integer > INT_MAX || obj.data.integer < INT_MIN) {
        set_api_error("Integer value outside range", err);
        return false;
      }

      tv->v_type = VAR_NUMBER;
      tv->vval.v_number = (int)obj.data.integer;
      break;

    case kObjectTypeFloat:
      tv->v_type = VAR_FLOAT;
      tv->vval.v_float = obj.data.floating;
      break;

    case kObjectTypeString:
      tv->v_type = VAR_STRING;
      tv->vval.v_string = xmemdupz(obj.data.string.data,
                                   obj.data.string.size);
      break;

    case kObjectTypeArray:
      tv->v_type = VAR_LIST;
      tv->vval.v_list = list_alloc();

      for (uint32_t i = 0; i < obj.data.array.size; i++) {
        Object item = obj.data.array.items[i];
        listitem_T *li = listitem_alloc();

        if (!object_to_vim(item, &li->li_tv, err)) {
          // cleanup
          listitem_free(li);
          list_free(tv->vval.v_list, true);
          return false;
        }

        list_append(tv->vval.v_list, li);
      }
      tv->vval.v_list->lv_refcount++;
      break;

    case kObjectTypeDictionary:
      tv->v_type = VAR_DICT;
      tv->vval.v_dict = dict_alloc();

      for (uint32_t i = 0; i < obj.data.dictionary.size; i++) {
        KeyValuePair item = obj.data.dictionary.items[i];
        String key = item.key;

        if (key.size == 0) {
          set_api_error("Empty dictionary keys aren't allowed", err);
          // cleanup
          dict_free(tv->vval.v_dict, true);
          return false;
        }

        dictitem_T *di = dictitem_alloc((uint8_t *) key.data);

        if (!object_to_vim(item.value, &di->di_tv, err)) {
          // cleanup
          dictitem_free(di);
          dict_free(tv->vval.v_dict, true);
          return false;
        }

        dict_add(tv->vval.v_dict, di);
      }
      tv->vval.v_dict->dv_refcount++;
      break;
  }

  return true;
}