mirror of
https://github.com/neovim/neovim.git
synced 2026-05-26 14:58:28 +00:00
Problem: filetype: ObjectScript are not recognized
Solution: Add ObjectScript filetype detection for *.cls files
(Hannah Kimura)).
Reference:
https://docs.intersystems.com/latest/csp/docbook/DocBook.UI.Page.cls?KEY=GCOS_intro
closes: vim/vim#19668
b11c8efbe6
Co-authored-by: Hannah <hannah.kimura@intersystems.com>
244 lines
5.5 KiB
C
244 lines
5.5 KiB
C
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "nvim/errors.h"
|
|
#include "nvim/eval.h"
|
|
#include "nvim/eval/executor.h"
|
|
#include "nvim/eval/typval.h"
|
|
#include "nvim/eval/typval_defs.h"
|
|
#include "nvim/garray.h"
|
|
#include "nvim/gettext_defs.h"
|
|
#include "nvim/message.h"
|
|
#include "nvim/strings.h"
|
|
#include "nvim/types_defs.h"
|
|
#include "nvim/vim_defs.h"
|
|
|
|
#include "eval/executor.c.generated.h"
|
|
|
|
/// Handle "blob1 += blob2".
|
|
/// Returns OK or FAIL.
|
|
static int tv_op_blob(typval_T *tv1, const typval_T *tv2, const char *op)
|
|
FUNC_ATTR_NONNULL_ALL
|
|
{
|
|
if (*op != '+' || tv2->v_type != VAR_BLOB) {
|
|
return FAIL;
|
|
}
|
|
|
|
// Blob += Blob
|
|
if (tv2->vval.v_blob == NULL) {
|
|
return OK;
|
|
}
|
|
|
|
if (tv1->vval.v_blob == NULL) {
|
|
tv1->vval.v_blob = tv2->vval.v_blob;
|
|
tv1->vval.v_blob->bv_refcount++;
|
|
return OK;
|
|
}
|
|
|
|
blob_T *const b1 = tv1->vval.v_blob;
|
|
blob_T *const b2 = tv2->vval.v_blob;
|
|
const int len = tv_blob_len(b2);
|
|
|
|
if (len > 0) {
|
|
ga_grow(&b1->bv_ga, len);
|
|
memmove((uint8_t *)b1->bv_ga.ga_data + b1->bv_ga.ga_len,
|
|
(uint8_t *)b2->bv_ga.ga_data, (size_t)len);
|
|
b1->bv_ga.ga_len += len;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/// Handle "list1 += list2".
|
|
/// Returns OK or FAIL.
|
|
static int tv_op_list(typval_T *tv1, const typval_T *tv2, const char *op)
|
|
FUNC_ATTR_NONNULL_ALL
|
|
{
|
|
if (*op != '+' || tv2->v_type != VAR_LIST) {
|
|
return FAIL;
|
|
}
|
|
|
|
// List += List
|
|
if (tv2->vval.v_list == NULL) {
|
|
return OK;
|
|
}
|
|
|
|
if (tv1->vval.v_list == NULL) {
|
|
tv1->vval.v_list = tv2->vval.v_list;
|
|
tv_list_ref(tv1->vval.v_list);
|
|
} else {
|
|
tv_list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL);
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/// Handle number operations:
|
|
/// nr += nr , nr -= nr , nr *=nr , nr /= nr , nr %= nr
|
|
///
|
|
/// Returns OK or FAIL.
|
|
static int tv_op_number(typval_T *tv1, const typval_T *tv2, const char *op)
|
|
FUNC_ATTR_NONNULL_ALL
|
|
{
|
|
varnumber_T n = tv_get_number(tv1);
|
|
if (tv2->v_type == VAR_FLOAT) {
|
|
float_T f = (float_T)n;
|
|
if (*op == '%') {
|
|
return FAIL;
|
|
}
|
|
switch (*op) {
|
|
case '+':
|
|
f += tv2->vval.v_float; break;
|
|
case '-':
|
|
f -= tv2->vval.v_float; break;
|
|
case '*':
|
|
f *= tv2->vval.v_float; break;
|
|
case '/':
|
|
f /= tv2->vval.v_float; break;
|
|
}
|
|
tv_clear(tv1);
|
|
tv1->v_type = VAR_FLOAT;
|
|
tv1->vval.v_float = f;
|
|
} else {
|
|
switch (*op) {
|
|
case '+':
|
|
n += tv_get_number(tv2); break;
|
|
case '-':
|
|
n -= tv_get_number(tv2); break;
|
|
case '*':
|
|
n *= tv_get_number(tv2); break;
|
|
case '/':
|
|
n = num_divide(n, tv_get_number(tv2)); break;
|
|
case '%':
|
|
n = num_modulus(n, tv_get_number(tv2)); break;
|
|
}
|
|
tv_clear(tv1);
|
|
tv1->v_type = VAR_NUMBER;
|
|
tv1->vval.v_number = n;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/// Handle "str1 .= str2"
|
|
/// Returns OK or FAIL.
|
|
static int tv_op_string(typval_T *tv1, const typval_T *tv2, const char *op)
|
|
FUNC_ATTR_NONNULL_ALL
|
|
{
|
|
if (tv2->v_type == VAR_FLOAT) {
|
|
return FAIL;
|
|
}
|
|
|
|
char numbuf[NUMBUFLEN];
|
|
// str .= str
|
|
const char *s2 = tv_get_string_buf(tv2, numbuf);
|
|
if (grow_string_tv(tv1, s2) == OK) {
|
|
return OK;
|
|
}
|
|
|
|
const char *tvs = tv_get_string(tv1);
|
|
char *const s = concat_str(tvs, s2);
|
|
tv_clear(tv1);
|
|
tv1->v_type = VAR_STRING;
|
|
tv1->vval.v_string = s;
|
|
|
|
return OK;
|
|
}
|
|
|
|
/// Handle "tv1 += tv2", "tv1 -= tv2", "tv1 *= tv2", "tv1 /= tv2", "tv1 %= tv2"
|
|
/// and "tv1 .= tv2"
|
|
/// Returns OK or FAIL.
|
|
static int tv_op_nr_or_string(typval_T *tv1, const typval_T *tv2, const char *op)
|
|
FUNC_ATTR_NONNULL_ALL
|
|
{
|
|
if (tv2->v_type == VAR_LIST) {
|
|
return FAIL;
|
|
}
|
|
|
|
if (vim_strchr("+-*/%", (uint8_t)(*op)) != NULL) {
|
|
return tv_op_number(tv1, tv2, op);
|
|
}
|
|
|
|
return tv_op_string(tv1, tv2, op);
|
|
}
|
|
|
|
/// Handle "f1 += f2", "f1 -= f2", "f1 *= f2", "f1 /= f2".
|
|
/// Returns OK or FAIL.
|
|
static int tv_op_float(typval_T *tv1, const typval_T *tv2, const char *op)
|
|
FUNC_ATTR_NONNULL_ALL
|
|
{
|
|
if (*op == '%' || *op == '.'
|
|
|| (tv2->v_type != VAR_FLOAT
|
|
&& tv2->v_type != VAR_NUMBER
|
|
&& tv2->v_type != VAR_STRING)) {
|
|
return FAIL;
|
|
}
|
|
|
|
const float_T f = (tv2->v_type == VAR_FLOAT
|
|
? tv2->vval.v_float
|
|
: (float_T)tv_get_number(tv2));
|
|
switch (*op) {
|
|
case '+':
|
|
tv1->vval.v_float += f; break;
|
|
case '-':
|
|
tv1->vval.v_float -= f; break;
|
|
case '*':
|
|
tv1->vval.v_float *= f; break;
|
|
case '/':
|
|
tv1->vval.v_float /= f; break;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
/// Handle tv1 += tv2, -=, *=, /=, %=, .=
|
|
///
|
|
/// @param[in,out] tv1 First operand, modified typval.
|
|
/// @param[in] tv2 Second operand.
|
|
/// @param[in] op Used operator.
|
|
///
|
|
/// @return OK or FAIL.
|
|
int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, const char *const op)
|
|
FUNC_ATTR_NONNULL_ALL
|
|
{
|
|
// Can't do anything with a Funcref or Dict on the right.
|
|
// v:true and friends only work with "..=".
|
|
if (tv2->v_type == VAR_FUNC || tv2->v_type == VAR_DICT
|
|
|| ((tv2->v_type == VAR_BOOL || tv2->v_type == VAR_SPECIAL) && *op == '.')) {
|
|
semsg(_(e_letwrong), op);
|
|
return FAIL;
|
|
}
|
|
|
|
int retval = FAIL;
|
|
|
|
switch (tv1->v_type) {
|
|
case VAR_DICT:
|
|
case VAR_FUNC:
|
|
case VAR_PARTIAL:
|
|
case VAR_BOOL:
|
|
case VAR_SPECIAL:
|
|
break;
|
|
case VAR_BLOB:
|
|
retval = tv_op_blob(tv1, tv2, op);
|
|
break;
|
|
case VAR_LIST:
|
|
retval = tv_op_list(tv1, tv2, op);
|
|
break;
|
|
case VAR_NUMBER:
|
|
case VAR_STRING:
|
|
retval = tv_op_nr_or_string(tv1, tv2, op);
|
|
break;
|
|
case VAR_FLOAT:
|
|
retval = tv_op_float(tv1, tv2, op);
|
|
break;
|
|
case VAR_UNKNOWN:
|
|
abort();
|
|
}
|
|
|
|
if (retval != OK) {
|
|
semsg(_(e_letwrong), op);
|
|
}
|
|
|
|
return retval;
|
|
}
|