diff options
Diffstat (limited to 'src')
58 files changed, 985 insertions, 1056 deletions
diff --git a/src/nvim/arabic.c b/src/nvim/arabic.c index 130ce65b86..06536e6e2b 100644 --- a/src/nvim/arabic.c +++ b/src/nvim/arabic.c @@ -5,6 +5,13 @@ /// /// Functions for Arabic language. /// +/// Author: Nadim Shaikli & Isam Bayazidi +/// Farsi support and restructuring to make adding new letters easier by Ali +/// Gholami Rudi. Further work by Ameretat Reith. + +/// Sorted list of unicode Arabic characters. Each entry holds the +/// presentation forms of a letter. +/// /// Arabic characters are categorized into following types: /// /// Isolated - iso-8859-6 form char denoted with a_* @@ -19,12 +26,7 @@ #include "nvim/ascii.h" #include "nvim/vim.h" -// Arabic ISO-10646-1 character set definition - -// Arabic ISO-8859-6 (subset of 10646; 0600 - 06FF) -#define a_COMMA 0x060C -#define a_SEMICOLON 0x061B -#define a_QUESTION 0x061F +// Unicode values for Arabic characters. #define a_HAMZA 0x0621 #define a_ALEF_MADDA 0x0622 #define a_ALEF_HAMZA_ABOVE 0x0623 @@ -62,7 +64,6 @@ #define a_WAW 0x0648 #define a_ALEF_MAKSURA 0x0649 #define a_YEH 0x064a - #define a_FATHATAN 0x064b #define a_DAMMATAN 0x064c #define a_KASRATAN 0x064d @@ -71,168 +72,17 @@ #define a_KASRA 0x0650 #define a_SHADDA 0x0651 #define a_SUKUN 0x0652 - #define a_MADDA_ABOVE 0x0653 #define a_HAMZA_ABOVE 0x0654 #define a_HAMZA_BELOW 0x0655 -#define a_ZERO 0x0660 -#define a_ONE 0x0661 -#define a_TWO 0x0662 -#define a_THREE 0x0663 -#define a_FOUR 0x0664 -#define a_FIVE 0x0665 -#define a_SIX 0x0666 -#define a_SEVEN 0x0667 -#define a_EIGHT 0x0668 -#define a_NINE 0x0669 -#define a_PERCENT 0x066a -#define a_DECIMAL 0x066b -#define a_THOUSANDS 0x066c -#define a_STAR 0x066d -#define a_MINI_ALEF 0x0670 -// Rest of 8859-6 does not relate to Arabic +#define a_PEH 0x067e +#define a_TCHEH 0x0686 +#define a_JEH 0x0698 +#define a_FKAF 0x06a9 +#define a_GAF 0x06af +#define a_FYEH 0x06cc -// Arabic Presentation Form-B (subset of 10646; FE70 - FEFF) -// -// s -> isolated -// i -> initial -// m -> medial -// f -> final -#define a_s_FATHATAN 0xfe70 -#define a_m_TATWEEL_FATHATAN 0xfe71 -#define a_s_DAMMATAN 0xfe72 - -#define a_s_KASRATAN 0xfe74 - -#define a_s_FATHA 0xfe76 -#define a_m_FATHA 0xfe77 -#define a_s_DAMMA 0xfe78 -#define a_m_DAMMA 0xfe79 -#define a_s_KASRA 0xfe7a -#define a_m_KASRA 0xfe7b -#define a_s_SHADDA 0xfe7c -#define a_m_SHADDA 0xfe7d -#define a_s_SUKUN 0xfe7e -#define a_m_SUKUN 0xfe7f - -#define a_s_HAMZA 0xfe80 -#define a_s_ALEF_MADDA 0xfe81 -#define a_f_ALEF_MADDA 0xfe82 -#define a_s_ALEF_HAMZA_ABOVE 0xfe83 -#define a_f_ALEF_HAMZA_ABOVE 0xfe84 -#define a_s_WAW_HAMZA 0xfe85 -#define a_f_WAW_HAMZA 0xfe86 -#define a_s_ALEF_HAMZA_BELOW 0xfe87 -#define a_f_ALEF_HAMZA_BELOW 0xfe88 -#define a_s_YEH_HAMZA 0xfe89 -#define a_f_YEH_HAMZA 0xfe8a -#define a_i_YEH_HAMZA 0xfe8b -#define a_m_YEH_HAMZA 0xfe8c -#define a_s_ALEF 0xfe8d -#define a_f_ALEF 0xfe8e -#define a_s_BEH 0xfe8f -#define a_f_BEH 0xfe90 -#define a_i_BEH 0xfe91 -#define a_m_BEH 0xfe92 -#define a_s_TEH_MARBUTA 0xfe93 -#define a_f_TEH_MARBUTA 0xfe94 -#define a_s_TEH 0xfe95 -#define a_f_TEH 0xfe96 -#define a_i_TEH 0xfe97 -#define a_m_TEH 0xfe98 -#define a_s_THEH 0xfe99 -#define a_f_THEH 0xfe9a -#define a_i_THEH 0xfe9b -#define a_m_THEH 0xfe9c -#define a_s_JEEM 0xfe9d -#define a_f_JEEM 0xfe9e -#define a_i_JEEM 0xfe9f -#define a_m_JEEM 0xfea0 -#define a_s_HAH 0xfea1 -#define a_f_HAH 0xfea2 -#define a_i_HAH 0xfea3 -#define a_m_HAH 0xfea4 -#define a_s_KHAH 0xfea5 -#define a_f_KHAH 0xfea6 -#define a_i_KHAH 0xfea7 -#define a_m_KHAH 0xfea8 -#define a_s_DAL 0xfea9 -#define a_f_DAL 0xfeaa -#define a_s_THAL 0xfeab -#define a_f_THAL 0xfeac -#define a_s_REH 0xfead -#define a_f_REH 0xfeae -#define a_s_ZAIN 0xfeaf -#define a_f_ZAIN 0xfeb0 -#define a_s_SEEN 0xfeb1 -#define a_f_SEEN 0xfeb2 -#define a_i_SEEN 0xfeb3 -#define a_m_SEEN 0xfeb4 -#define a_s_SHEEN 0xfeb5 -#define a_f_SHEEN 0xfeb6 -#define a_i_SHEEN 0xfeb7 -#define a_m_SHEEN 0xfeb8 -#define a_s_SAD 0xfeb9 -#define a_f_SAD 0xfeba -#define a_i_SAD 0xfebb -#define a_m_SAD 0xfebc -#define a_s_DAD 0xfebd -#define a_f_DAD 0xfebe -#define a_i_DAD 0xfebf -#define a_m_DAD 0xfec0 -#define a_s_TAH 0xfec1 -#define a_f_TAH 0xfec2 -#define a_i_TAH 0xfec3 -#define a_m_TAH 0xfec4 -#define a_s_ZAH 0xfec5 -#define a_f_ZAH 0xfec6 -#define a_i_ZAH 0xfec7 -#define a_m_ZAH 0xfec8 -#define a_s_AIN 0xfec9 -#define a_f_AIN 0xfeca -#define a_i_AIN 0xfecb -#define a_m_AIN 0xfecc -#define a_s_GHAIN 0xfecd -#define a_f_GHAIN 0xfece -#define a_i_GHAIN 0xfecf -#define a_m_GHAIN 0xfed0 -#define a_s_FEH 0xfed1 -#define a_f_FEH 0xfed2 -#define a_i_FEH 0xfed3 -#define a_m_FEH 0xfed4 -#define a_s_QAF 0xfed5 -#define a_f_QAF 0xfed6 -#define a_i_QAF 0xfed7 -#define a_m_QAF 0xfed8 -#define a_s_KAF 0xfed9 -#define a_f_KAF 0xfeda -#define a_i_KAF 0xfedb -#define a_m_KAF 0xfedc -#define a_s_LAM 0xfedd -#define a_f_LAM 0xfede -#define a_i_LAM 0xfedf -#define a_m_LAM 0xfee0 -#define a_s_MEEM 0xfee1 -#define a_f_MEEM 0xfee2 -#define a_i_MEEM 0xfee3 -#define a_m_MEEM 0xfee4 -#define a_s_NOON 0xfee5 -#define a_f_NOON 0xfee6 -#define a_i_NOON 0xfee7 -#define a_m_NOON 0xfee8 -#define a_s_HEH 0xfee9 -#define a_f_HEH 0xfeea -#define a_i_HEH 0xfeeb -#define a_m_HEH 0xfeec -#define a_s_WAW 0xfeed -#define a_f_WAW 0xfeee -#define a_s_ALEF_MAKSURA 0xfeef -#define a_f_ALEF_MAKSURA 0xfef0 -#define a_s_YEH 0xfef1 -#define a_f_YEH 0xfef2 -#define a_i_YEH 0xfef3 -#define a_m_YEH 0xfef4 #define a_s_LAM_ALEF_MADDA_ABOVE 0xfef5 #define a_f_LAM_ALEF_MADDA_ABOVE 0xfef6 #define a_s_LAM_ALEF_HAMZA_ABOVE 0xfef7 @@ -242,664 +92,201 @@ #define a_s_LAM_ALEF 0xfefb #define a_f_LAM_ALEF 0xfefc +static struct achar { + unsigned c; + unsigned isolated; + unsigned initial; + unsigned medial; + unsigned final; +} achars[] = { + { a_HAMZA, 0xfe80, 0, 0, 0 }, + { a_ALEF_MADDA, 0xfe81, 0, 0, 0xfe82 }, + { a_ALEF_HAMZA_ABOVE, 0xfe83, 0, 0, 0xfe84 }, + { a_WAW_HAMZA, 0xfe85, 0, 0, 0xfe86 }, + { a_ALEF_HAMZA_BELOW, 0xfe87, 0, 0, 0xfe88 }, + { a_YEH_HAMZA, 0xfe89, 0xfe8b, 0xfe8c, 0xfe8a }, + { a_ALEF, 0xfe8d, 0, 0, 0xfe8e }, + { a_BEH, 0xfe8f, 0xfe91, 0xfe92, 0xfe90 }, + { a_TEH_MARBUTA, 0xfe93, 0, 0, 0xfe94 }, + { a_TEH, 0xfe95, 0xfe97, 0xfe98, 0xfe96 }, + { a_THEH, 0xfe99, 0xfe9b, 0xfe9c, 0xfe9a }, + { a_JEEM, 0xfe9d, 0xfe9f, 0xfea0, 0xfe9e }, + { a_HAH, 0xfea1, 0xfea3, 0xfea4, 0xfea2 }, + { a_KHAH, 0xfea5, 0xfea7, 0xfea8, 0xfea6 }, + { a_DAL, 0xfea9, 0, 0, 0xfeaa }, + { a_THAL, 0xfeab, 0, 0, 0xfeac }, + { a_REH, 0xfead, 0, 0, 0xfeae }, + { a_ZAIN, 0xfeaf, 0, 0, 0xfeb0 }, + { a_SEEN, 0xfeb1, 0xfeb3, 0xfeb4, 0xfeb2 }, + { a_SHEEN, 0xfeb5, 0xfeb7, 0xfeb8, 0xfeb6 }, + { a_SAD, 0xfeb9, 0xfebb, 0xfebc, 0xfeba }, + { a_DAD, 0xfebd, 0xfebf, 0xfec0, 0xfebe }, + { a_TAH, 0xfec1, 0xfec3, 0xfec4, 0xfec2 }, + { a_ZAH, 0xfec5, 0xfec7, 0xfec8, 0xfec6 }, + { a_AIN, 0xfec9, 0xfecb, 0xfecc, 0xfeca }, + { a_GHAIN, 0xfecd, 0xfecf, 0xfed0, 0xfece }, + { a_TATWEEL, 0, 0x0640, 0x0640, 0x0640 }, + { a_FEH, 0xfed1, 0xfed3, 0xfed4, 0xfed2 }, + { a_QAF, 0xfed5, 0xfed7, 0xfed8, 0xfed6 }, + { a_KAF, 0xfed9, 0xfedb, 0xfedc, 0xfeda }, + { a_LAM, 0xfedd, 0xfedf, 0xfee0, 0xfede }, + { a_MEEM, 0xfee1, 0xfee3, 0xfee4, 0xfee2 }, + { a_NOON, 0xfee5, 0xfee7, 0xfee8, 0xfee6 }, + { a_HEH, 0xfee9, 0xfeeb, 0xfeec, 0xfeea }, + { a_WAW, 0xfeed, 0, 0, 0xfeee }, + { a_ALEF_MAKSURA, 0xfeef, 0, 0, 0xfef0 }, + { a_YEH, 0xfef1, 0xfef3, 0xfef4, 0xfef2 }, + { a_FATHATAN, 0xfe70, 0, 0, 0 }, + { a_DAMMATAN, 0xfe72, 0, 0, 0 }, + { a_KASRATAN, 0xfe74, 0, 0, 0 }, + { a_FATHA, 0xfe76, 0, 0xfe77, 0 }, + { a_DAMMA, 0xfe78, 0, 0xfe79, 0 }, + { a_KASRA, 0xfe7a, 0, 0xfe7b, 0 }, + { a_SHADDA, 0xfe7c, 0, 0xfe7c, 0 }, + { a_SUKUN, 0xfe7e, 0, 0xfe7f, 0 }, + { a_MADDA_ABOVE, 0, 0, 0, 0 }, + { a_HAMZA_ABOVE, 0, 0, 0, 0 }, + { a_HAMZA_BELOW, 0, 0, 0, 0 }, + { a_PEH, 0xfb56, 0xfb58, 0xfb59, 0xfb57 }, + { a_TCHEH, 0xfb7a, 0xfb7c, 0xfb7d, 0xfb7b }, + { a_JEH, 0xfb8a, 0, 0, 0xfb8b }, + { a_FKAF, 0xfb8e, 0xfb90, 0xfb91, 0xfb8f }, + { a_GAF, 0xfb92, 0xfb94, 0xfb95, 0xfb93 }, + { a_FYEH, 0xfbfc, 0xfbfe, 0xfbff, 0xfbfd }, +}; + #define a_BYTE_ORDER_MARK 0xfeff #ifdef INCLUDE_GENERATED_DECLARATIONS # include "arabic.c.generated.h" #endif -// Returns true if c is an ISO-8859-6 shaped ARABIC letter (user entered). -static bool A_is_a(int cur_c) -{ - switch (cur_c) { - case a_HAMZA: - case a_ALEF_MADDA: - case a_ALEF_HAMZA_ABOVE: - case a_WAW_HAMZA: - case a_ALEF_HAMZA_BELOW: - case a_YEH_HAMZA: - case a_ALEF: - case a_BEH: - case a_TEH_MARBUTA: - case a_TEH: - case a_THEH: - case a_JEEM: - case a_HAH: - case a_KHAH: - case a_DAL: - case a_THAL: - case a_REH: - case a_ZAIN: - case a_SEEN: - case a_SHEEN: - case a_SAD: - case a_DAD: - case a_TAH: - case a_ZAH: - case a_AIN: - case a_GHAIN: - case a_TATWEEL: - case a_FEH: - case a_QAF: - case a_KAF: - case a_LAM: - case a_MEEM: - case a_NOON: - case a_HEH: - case a_WAW: - case a_ALEF_MAKSURA: - case a_YEH: - return true; - } - - return false; -} - -// Returns true if c is an Isolated Form-B ARABIC letter -static bool A_is_s(int cur_c) +/// Find the struct achar pointer to the given Arabic char. +/// Returns NULL if not found. +static struct achar *find_achar(int c) { - switch (cur_c) { - case a_s_HAMZA: - case a_s_ALEF_MADDA: - case a_s_ALEF_HAMZA_ABOVE: - case a_s_WAW_HAMZA: - case a_s_ALEF_HAMZA_BELOW: - case a_s_YEH_HAMZA: - case a_s_ALEF: - case a_s_BEH: - case a_s_TEH_MARBUTA: - case a_s_TEH: - case a_s_THEH: - case a_s_JEEM: - case a_s_HAH: - case a_s_KHAH: - case a_s_DAL: - case a_s_THAL: - case a_s_REH: - case a_s_ZAIN: - case a_s_SEEN: - case a_s_SHEEN: - case a_s_SAD: - case a_s_DAD: - case a_s_TAH: - case a_s_ZAH: - case a_s_AIN: - case a_s_GHAIN: - case a_s_FEH: - case a_s_QAF: - case a_s_KAF: - case a_s_LAM: - case a_s_MEEM: - case a_s_NOON: - case a_s_HEH: - case a_s_WAW: - case a_s_ALEF_MAKSURA: - case a_s_YEH: - return true; + // using binary search to find c + int h = ARRAY_SIZE(achars); + int l = 0; + while (l < h) { + int m = (h + l) / 2; + if (achars[m].c == (unsigned)c) { + return &achars[m]; + } + if ((unsigned)c < achars[m].c) { + h = m; + } else { + l = m + 1; + } } - - return false; + return NULL; } -// Returns true if c is a Final shape of an ARABIC letter -static bool A_is_f(int cur_c) +/// Change shape - from Combination (2 char) to an Isolated +static int chg_c_laa2i(int hid_c) { - switch (cur_c) { - case a_f_ALEF_MADDA: - case a_f_ALEF_HAMZA_ABOVE: - case a_f_WAW_HAMZA: - case a_f_ALEF_HAMZA_BELOW: - case a_f_YEH_HAMZA: - case a_f_ALEF: - case a_f_BEH: - case a_f_TEH_MARBUTA: - case a_f_TEH: - case a_f_THEH: - case a_f_JEEM: - case a_f_HAH: - case a_f_KHAH: - case a_f_DAL: - case a_f_THAL: - case a_f_REH: - case a_f_ZAIN: - case a_f_SEEN: - case a_f_SHEEN: - case a_f_SAD: - case a_f_DAD: - case a_f_TAH: - case a_f_ZAH: - case a_f_AIN: - case a_f_GHAIN: - case a_f_FEH: - case a_f_QAF: - case a_f_KAF: - case a_f_LAM: - case a_f_MEEM: - case a_f_NOON: - case a_f_HEH: - case a_f_WAW: - case a_f_ALEF_MAKSURA: - case a_f_YEH: - case a_f_LAM_ALEF_MADDA_ABOVE: - case a_f_LAM_ALEF_HAMZA_ABOVE: - case a_f_LAM_ALEF_HAMZA_BELOW: - case a_f_LAM_ALEF: - return true; - } - return false; -} + int tempc; -// Change shape - from ISO-8859-6/Isolated to Form-B Isolated -static int chg_c_a2s(int cur_c) -{ - switch (cur_c) { - case a_HAMZA: - return a_s_HAMZA; + switch (hid_c) { case a_ALEF_MADDA: - return a_s_ALEF_MADDA; + tempc = a_s_LAM_ALEF_MADDA_ABOVE; + break; case a_ALEF_HAMZA_ABOVE: - return a_s_ALEF_HAMZA_ABOVE; - case a_WAW_HAMZA: - return a_s_WAW_HAMZA; + tempc = a_s_LAM_ALEF_HAMZA_ABOVE; + break; case a_ALEF_HAMZA_BELOW: - return a_s_ALEF_HAMZA_BELOW; - case a_YEH_HAMZA: - return a_s_YEH_HAMZA; + tempc = a_s_LAM_ALEF_HAMZA_BELOW; + break; case a_ALEF: - return a_s_ALEF; - case a_TEH_MARBUTA: - return a_s_TEH_MARBUTA; - case a_DAL: - return a_s_DAL; - case a_THAL: - return a_s_THAL; - case a_REH: - return a_s_REH; - case a_ZAIN: - return a_s_ZAIN; - case a_TATWEEL: - return cur_c; // exceptions - case a_WAW: - return a_s_WAW; - case a_ALEF_MAKSURA: - return a_s_ALEF_MAKSURA; - case a_BEH: - return a_s_BEH; - case a_TEH: - return a_s_TEH; - case a_THEH: - return a_s_THEH; - case a_JEEM: - return a_s_JEEM; - case a_HAH: - return a_s_HAH; - case a_KHAH: - return a_s_KHAH; - case a_SEEN: - return a_s_SEEN; - case a_SHEEN: - return a_s_SHEEN; - case a_SAD: - return a_s_SAD; - case a_DAD: - return a_s_DAD; - case a_TAH: - return a_s_TAH; - case a_ZAH: - return a_s_ZAH; - case a_AIN: - return a_s_AIN; - case a_GHAIN: - return a_s_GHAIN; - case a_FEH: - return a_s_FEH; - case a_QAF: - return a_s_QAF; - case a_KAF: - return a_s_KAF; - case a_LAM: - return a_s_LAM; - case a_MEEM: - return a_s_MEEM; - case a_NOON: - return a_s_NOON; - case a_HEH: - return a_s_HEH; - case a_YEH: - return a_s_YEH; + tempc = a_s_LAM_ALEF; + break; + default: + tempc = 0; } - return 0; -} -// Change shape - from ISO-8859-6/Isolated to Initial -static int chg_c_a2i(int cur_c) -{ - switch (cur_c) { - case a_YEH_HAMZA: - return a_i_YEH_HAMZA; - case a_HAMZA: - return a_s_HAMZA; // exceptions - case a_ALEF_MADDA: - return a_s_ALEF_MADDA; // exceptions - case a_ALEF_HAMZA_ABOVE: - return a_s_ALEF_HAMZA_ABOVE; // exceptions - case a_WAW_HAMZA: - return a_s_WAW_HAMZA; // exceptions - case a_ALEF_HAMZA_BELOW: - return a_s_ALEF_HAMZA_BELOW; // exceptions - case a_ALEF: - return a_s_ALEF; // exceptions - case a_TEH_MARBUTA: - return a_s_TEH_MARBUTA; // exceptions - case a_DAL: - return a_s_DAL; // exceptions - case a_THAL: - return a_s_THAL; // exceptions - case a_REH: - return a_s_REH; // exceptions - case a_ZAIN: - return a_s_ZAIN; // exceptions - case a_TATWEEL: - return cur_c; // exceptions - case a_WAW: - return a_s_WAW; // exceptions - case a_ALEF_MAKSURA: - return a_s_ALEF_MAKSURA; // exceptions - case a_BEH: - return a_i_BEH; - case a_TEH: - return a_i_TEH; - case a_THEH: - return a_i_THEH; - case a_JEEM: - return a_i_JEEM; - case a_HAH: - return a_i_HAH; - case a_KHAH: - return a_i_KHAH; - case a_SEEN: - return a_i_SEEN; - case a_SHEEN: - return a_i_SHEEN; - case a_SAD: - return a_i_SAD; - case a_DAD: - return a_i_DAD; - case a_TAH: - return a_i_TAH; - case a_ZAH: - return a_i_ZAH; - case a_AIN: - return a_i_AIN; - case a_GHAIN: - return a_i_GHAIN; - case a_FEH: - return a_i_FEH; - case a_QAF: - return a_i_QAF; - case a_KAF: - return a_i_KAF; - case a_LAM: - return a_i_LAM; - case a_MEEM: - return a_i_MEEM; - case a_NOON: - return a_i_NOON; - case a_HEH: - return a_i_HEH; - case a_YEH: - return a_i_YEH; - } - return 0; + return tempc; } -// Change shape - from ISO-8859-6/Isolated to Medial -static int chg_c_a2m(int cur_c) +/// Change shape - from Combination-Isolated to Final +static int chg_c_laa2f(int hid_c) { - switch (cur_c) { - case a_HAMZA: - return a_s_HAMZA; // exception + int tempc; + + switch (hid_c) { case a_ALEF_MADDA: - return a_f_ALEF_MADDA; // exception + tempc = a_f_LAM_ALEF_MADDA_ABOVE; + break; case a_ALEF_HAMZA_ABOVE: - return a_f_ALEF_HAMZA_ABOVE; // exception - case a_WAW_HAMZA: - return a_f_WAW_HAMZA; // exception + tempc = a_f_LAM_ALEF_HAMZA_ABOVE; + break; case a_ALEF_HAMZA_BELOW: - return a_f_ALEF_HAMZA_BELOW; // exception - case a_YEH_HAMZA: - return a_m_YEH_HAMZA; + tempc = a_f_LAM_ALEF_HAMZA_BELOW; + break; case a_ALEF: - return a_f_ALEF; // exception - case a_BEH: - return a_m_BEH; - case a_TEH_MARBUTA: - return a_f_TEH_MARBUTA; // exception - case a_TEH: - return a_m_TEH; - case a_THEH: - return a_m_THEH; - case a_JEEM: - return a_m_JEEM; - case a_HAH: - return a_m_HAH; - case a_KHAH: - return a_m_KHAH; - case a_DAL: - return a_f_DAL; // exception - case a_THAL: - return a_f_THAL; // exception - case a_REH: - return a_f_REH; // exception - case a_ZAIN: - return a_f_ZAIN; // exception - case a_SEEN: - return a_m_SEEN; - case a_SHEEN: - return a_m_SHEEN; - case a_SAD: - return a_m_SAD; - case a_DAD: - return a_m_DAD; - case a_TAH: - return a_m_TAH; - case a_ZAH: - return a_m_ZAH; - case a_AIN: - return a_m_AIN; - case a_GHAIN: - return a_m_GHAIN; - case a_TATWEEL: - return cur_c; // exception - case a_FEH: - return a_m_FEH; - case a_QAF: - return a_m_QAF; - case a_KAF: - return a_m_KAF; - case a_LAM: - return a_m_LAM; - case a_MEEM: - return a_m_MEEM; - case a_NOON: - return a_m_NOON; - case a_HEH: - return a_m_HEH; - case a_WAW: - return a_f_WAW; // exception - case a_ALEF_MAKSURA: - return a_f_ALEF_MAKSURA; // exception - case a_YEH: - return a_m_YEH; + tempc = a_f_LAM_ALEF; + break; + default: + tempc = 0; } - return 0; + + return tempc; } -// Change shape - from ISO-8859-6/Isolated to final -static int chg_c_a2f(int cur_c) +/// Returns whether it is possible to join the given letters +static int can_join(int c1, int c2) { - // NOTE: these encodings need to be accounted for - // - // a_f_ALEF_MADDA; - // a_f_ALEF_HAMZA_ABOVE; - // a_f_ALEF_HAMZA_BELOW; - // a_f_LAM_ALEF_MADDA_ABOVE; - // a_f_LAM_ALEF_HAMZA_ABOVE; - // a_f_LAM_ALEF_HAMZA_BELOW; + struct achar *a1 = find_achar(c1); + struct achar *a2 = find_achar(c2); - switch (cur_c) { - case a_HAMZA: - return a_s_HAMZA; // exception - case a_ALEF_MADDA: - return a_f_ALEF_MADDA; - case a_ALEF_HAMZA_ABOVE: - return a_f_ALEF_HAMZA_ABOVE; - case a_WAW_HAMZA: - return a_f_WAW_HAMZA; - case a_ALEF_HAMZA_BELOW: - return a_f_ALEF_HAMZA_BELOW; - case a_YEH_HAMZA: - return a_f_YEH_HAMZA; - case a_ALEF: - return a_f_ALEF; - case a_BEH: - return a_f_BEH; - case a_TEH_MARBUTA: - return a_f_TEH_MARBUTA; - case a_TEH: - return a_f_TEH; - case a_THEH: - return a_f_THEH; - case a_JEEM: - return a_f_JEEM; - case a_HAH: - return a_f_HAH; - case a_KHAH: - return a_f_KHAH; - case a_DAL: - return a_f_DAL; - case a_THAL: - return a_f_THAL; - case a_REH: - return a_f_REH; - case a_ZAIN: - return a_f_ZAIN; - case a_SEEN: - return a_f_SEEN; - case a_SHEEN: - return a_f_SHEEN; - case a_SAD: - return a_f_SAD; - case a_DAD: - return a_f_DAD; - case a_TAH: - return a_f_TAH; - case a_ZAH: - return a_f_ZAH; - case a_AIN: - return a_f_AIN; - case a_GHAIN: - return a_f_GHAIN; - case a_TATWEEL: - return cur_c; // exception - case a_FEH: - return a_f_FEH; - case a_QAF: - return a_f_QAF; - case a_KAF: - return a_f_KAF; - case a_LAM: - return a_f_LAM; - case a_MEEM: - return a_f_MEEM; - case a_NOON: - return a_f_NOON; - case a_HEH: - return a_f_HEH; - case a_WAW: - return a_f_WAW; - case a_ALEF_MAKSURA: - return a_f_ALEF_MAKSURA; - case a_YEH: - return a_f_YEH; - } - return 0; + return a1 && a2 && (a1->initial || a1->medial) && (a2->final || a2->medial); } -// Change shape - from Initial to Medial -// This code is unreachable, because for the relevant characters ARABIC_CHAR() -// is FALSE; -#if 0 -static int chg_c_i2m(int cur_c) +/// Check whether we are dealing with a character that could be regarded as an +/// Arabic combining character, need to check the character before this. +bool arabic_maycombine(int two) + FUNC_ATTR_PURE { - switch (cur_c) { - case a_i_YEH_HAMZA: - return a_m_YEH_HAMZA; - case a_i_BEH: - return a_m_BEH; - case a_i_TEH: - return a_m_TEH; - case a_i_THEH: - return a_m_THEH; - case a_i_JEEM: - return a_m_JEEM; - case a_i_HAH: - return a_m_HAH; - case a_i_KHAH: - return a_m_KHAH; - case a_i_SEEN: - return a_m_SEEN; - case a_i_SHEEN: - return a_m_SHEEN; - case a_i_SAD: - return a_m_SAD; - case a_i_DAD: - return a_m_DAD; - case a_i_TAH: - return a_m_TAH; - case a_i_ZAH: - return a_m_ZAH; - case a_i_AIN: - return a_m_AIN; - case a_i_GHAIN: - return a_m_GHAIN; - case a_i_FEH: - return a_m_FEH; - case a_i_QAF: - return a_m_QAF; - case a_i_KAF: - return a_m_KAF; - case a_i_LAM: - return a_m_LAM; - case a_i_MEEM: - return a_m_MEEM; - case a_i_NOON: - return a_m_NOON; - case a_i_HEH: - return a_m_HEH; - case a_i_YEH: - return a_m_YEH; + if (p_arshape && !p_tbidi) { + return two == a_ALEF_MADDA + || two == a_ALEF_HAMZA_ABOVE + || two == a_ALEF_HAMZA_BELOW + || two == a_ALEF; } - return 0; + return false; } -#endif -// Change shape - from Final to Medial -static int chg_c_f2m(int cur_c) +/// Check whether we are dealing with Arabic combining characters. +/// Note: these are NOT really composing characters! +/// +/// @param one First character. +/// @param two Character just after "one". +bool arabic_combine(int one, int two) + FUNC_ATTR_PURE { - switch (cur_c) { - // NOTE: these encodings are multi-positional, no ? - // case a_f_ALEF_MADDA: - // case a_f_ALEF_HAMZA_ABOVE: - // case a_f_ALEF_HAMZA_BELOW: - case a_f_YEH_HAMZA: - return a_m_YEH_HAMZA; - case a_f_WAW_HAMZA: // exceptions - case a_f_ALEF: - case a_f_TEH_MARBUTA: - case a_f_DAL: - case a_f_THAL: - case a_f_REH: - case a_f_ZAIN: - case a_f_WAW: - case a_f_ALEF_MAKSURA: - return cur_c; - case a_f_BEH: - return a_m_BEH; - case a_f_TEH: - return a_m_TEH; - case a_f_THEH: - return a_m_THEH; - case a_f_JEEM: - return a_m_JEEM; - case a_f_HAH: - return a_m_HAH; - case a_f_KHAH: - return a_m_KHAH; - case a_f_SEEN: - return a_m_SEEN; - case a_f_SHEEN: - return a_m_SHEEN; - case a_f_SAD: - return a_m_SAD; - case a_f_DAD: - return a_m_DAD; - case a_f_TAH: - return a_m_TAH; - case a_f_ZAH: - return a_m_ZAH; - case a_f_AIN: - return a_m_AIN; - case a_f_GHAIN: - return a_m_GHAIN; - case a_f_FEH: - return a_m_FEH; - case a_f_QAF: - return a_m_QAF; - case a_f_KAF: - return a_m_KAF; - case a_f_LAM: - return a_m_LAM; - case a_f_MEEM: - return a_m_MEEM; - case a_f_NOON: - return a_m_NOON; - case a_f_HEH: - return a_m_HEH; - case a_f_YEH: - return a_m_YEH; - // NOTE: these encodings are multi-positional, no ? - // case a_f_LAM_ALEF_MADDA_ABOVE: - // case a_f_LAM_ALEF_HAMZA_ABOVE: - // case a_f_LAM_ALEF_HAMZA_BELOW: - // case a_f_LAM_ALEF: + if (one == a_LAM) { + return arabic_maycombine(two); } - return 0; + return false; } -// Change shape - from Combination (2 char) to an Isolated. -static int chg_c_laa2i(int hid_c) +/// A_is_iso returns true if 'c' is an Arabic ISO-8859-6 character +/// (alphabet/number/punctuation) +static int A_is_iso(int c) { - switch (hid_c) { - case a_ALEF_MADDA: - return a_s_LAM_ALEF_MADDA_ABOVE; - case a_ALEF_HAMZA_ABOVE: - return a_s_LAM_ALEF_HAMZA_ABOVE; - case a_ALEF_HAMZA_BELOW: - return a_s_LAM_ALEF_HAMZA_BELOW; - case a_ALEF: - return a_s_LAM_ALEF; - } - return 0; + return find_achar(c) != NULL; } -// Change shape - from Combination-Isolated to Final. -static int chg_c_laa2f(int hid_c) +/// A_is_ok returns true if 'c' is an Arabic 10646 (8859-6 or Form-B) +static int A_is_ok(int c) { - switch (hid_c) { - case a_ALEF_MADDA: - return a_f_LAM_ALEF_MADDA_ABOVE; - case a_ALEF_HAMZA_ABOVE: - return a_f_LAM_ALEF_HAMZA_ABOVE; - case a_ALEF_HAMZA_BELOW: - return a_f_LAM_ALEF_HAMZA_BELOW; - case a_ALEF: - return a_f_LAM_ALEF; - } - return 0; + return (A_is_iso(c) || c == a_BYTE_ORDER_MARK); } -// Do "half-shaping" on character "c". Return zero if no shaping. -static int half_shape(int c) +/// A_is_valid returns true if 'c' is an Arabic 10646 (8859-6 or Form-B) +/// with some exceptions/exclusions +static int A_is_valid(int c) { - if (A_is_a(c)) { - return chg_c_a2i(c); - } - - if (A_is_valid(c) && A_is_f(c)) { - return chg_c_f2m(c); - } - return 0; + return (A_is_ok(c) && c != a_HAMZA); } // Do Arabic shaping on character "c". Returns the shaped character. @@ -916,37 +303,35 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, int next_c) return c; } - // half-shape current and previous character - int shape_c = half_shape(prev_c); - int curr_c; - int curr_laa = A_firstc_laa(c, *c1p); - int prev_laa = A_firstc_laa(prev_c, prev_c1); + int curr_laa = arabic_combine(c, *c1p); + int prev_laa = arabic_combine(prev_c, prev_c1); if (curr_laa) { - if (A_is_valid(prev_c) && !A_is_f(shape_c) && !A_is_s(shape_c) - && !prev_laa) { - curr_c = chg_c_laa2f(curr_laa); + if (A_is_valid(prev_c) && can_join(prev_c, a_LAM) && !prev_laa) { + curr_c = chg_c_laa2f(*c1p); } else { - curr_c = chg_c_laa2i(curr_laa); + curr_c = chg_c_laa2i(*c1p); } - // Remove the composing character *c1p = 0; - } else if (!A_is_valid(prev_c) && A_is_valid(next_c)) { - curr_c = chg_c_a2i(c); - } else if (!shape_c || A_is_f(shape_c) || A_is_s(shape_c) || prev_laa) { - curr_c = A_is_valid(next_c) ? chg_c_a2i(c) : chg_c_a2s(c); - } else if (A_is_valid(next_c)) { -#if 0 - curr_c = A_is_iso(c) ? chg_c_a2m(c) : chg_c_i2m(c); -#else - curr_c = A_is_iso(c) ? chg_c_a2m(c) : 0; -#endif - } else if (A_is_valid(prev_c)) { - curr_c = chg_c_a2f(c); } else { - curr_c = chg_c_a2s(c); + struct achar *curr_a = find_achar(c); + int backward_combine = !prev_laa && can_join(prev_c, c); + int forward_combine = can_join(c, next_c); + + if (backward_combine && forward_combine) { + curr_c = (int)curr_a->medial; + } + if (backward_combine && !forward_combine) { + curr_c = (int)curr_a->final; + } + if (!backward_combine && forward_combine) { + curr_c = (int)curr_a->initial; + } + if (!backward_combine && !forward_combine) { + curr_c = (int)curr_a->isolated; + } } // Sanity check -- curr_c should, in the future, never be 0. @@ -966,88 +351,3 @@ int arabic_shape(int c, int *ccp, int *c1p, int prev_c, int prev_c1, int next_c) // Return the shaped character return curr_c; } - -/// Check whether we are dealing with Arabic combining characters. -/// Note: these are NOT really composing characters! -/// -/// @param one First character. -/// @param two Character just after "one". -bool arabic_combine(int one, int two) - FUNC_ATTR_PURE -{ - if (one == a_LAM) { - return arabic_maycombine(two); - } - return false; -} - -/// Check whether we are dealing with a character that could be regarded as an -/// Arabic combining character, need to check the character before this. -bool arabic_maycombine(int two) - FUNC_ATTR_PURE -{ - if (p_arshape && !p_tbidi) { - return two == a_ALEF_MADDA - || two == a_ALEF_HAMZA_ABOVE - || two == a_ALEF_HAMZA_BELOW - || two == a_ALEF; - } - return false; -} - -// A_firstc_laa returns first character of LAA combination if it exists -// in: "c" base character -// in: "c1" first composing character -static int A_firstc_laa(int c, int c1) -{ - if ((c1 != NUL) && (c == a_LAM) && !A_is_harakat(c1)) { - return c1; - } - return 0; -} - -// A_is_harakat returns true if 'c' is an Arabic Harakat character. -// (harakat/tanween) -static bool A_is_harakat(int c) -{ - return c >= a_FATHATAN && c <= a_SUKUN; -} - -// A_is_iso returns true if 'c' is an Arabic ISO-8859-6 character. -// (alphabet/number/punctuation) -static bool A_is_iso(int c) -{ - return ((c >= a_HAMZA && c <= a_GHAIN) - || (c >= a_TATWEEL && c <= a_HAMZA_BELOW) - || c == a_MINI_ALEF); -} - -// A_is_formb returns true if 'c' is an Arabic 10646-1 FormB character. -// (alphabet/number/punctuation) -static bool A_is_formb(int c) -{ - return ((c >= a_s_FATHATAN && c <= a_s_DAMMATAN) - || c == a_s_KASRATAN - || (c >= a_s_FATHA && c <= a_f_LAM_ALEF) - || c == a_BYTE_ORDER_MARK); -} - -// A_is_ok returns true if 'c' is an Arabic 10646 (8859-6 or Form-B). -static bool A_is_ok(int c) -{ - return A_is_iso(c) || A_is_formb(c); -} - -// A_is_valid returns true if 'c' is an Arabic 10646 (8859-6 or Form-B), -// with some exceptions/exclusions. -static bool A_is_valid(int c) -{ - return A_is_ok(c) && !A_is_special(c); -} - -// A_is_special returns true if 'c' is not a special Arabic character. -// Specials don't adhere to most of the rules. -static bool A_is_special(int c) -{ - return c == a_HAMZA || c == a_s_HAMZA; -} diff --git a/src/nvim/arabic.h b/src/nvim/arabic.h index eaab463777..3c34de1449 100644 --- a/src/nvim/arabic.h +++ b/src/nvim/arabic.h @@ -3,12 +3,7 @@ #include <stdbool.h> -/// Whether c belongs to the range of Arabic characters that might be shaped. -static inline bool arabic_char(int c) -{ - // return c >= a_HAMZA && c <= a_MINI_ALEF; - return c >= 0x0621 && c <= 0x0670; -} +#define ARABIC_CHAR(ch) (((ch) & 0xFF00) == 0x0600) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "arabic.h.generated.h" diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index defe22ea9a..897928abec 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2156,31 +2156,23 @@ scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx) sctx_T script_sctx = { .sc_seq = ++last_current_SID_seq, .sc_lnum = 0, .sc_sid = 0 }; - FileID file_id; scriptitem_T *si = NULL; - bool file_id_ok = os_fileid((char *)fname, &file_id); assert(script_items.ga_len >= 0); - for (script_sctx.sc_sid = script_items.ga_len; script_sctx.sc_sid > 0; - script_sctx.sc_sid--) { + for (script_sctx.sc_sid = script_items.ga_len; script_sctx.sc_sid > 0; script_sctx.sc_sid--) { + // We used to check inode here, but that doesn't work: + // - If a script is edited and written, it may get a different + // inode number, even though to the user it is the same script. + // - If a script is deleted and another script is written, with a + // different name, the inode may be re-used. si = &SCRIPT_ITEM(script_sctx.sc_sid); - // Compare dev/ino when possible, it catches symbolic links. - // Also compare file names, the inode may change when the file was edited. - bool file_id_equal = file_id_ok && si->file_id_valid - && os_fileid_equal(&(si->file_id), &file_id); - if (si->sn_name != NULL - && (file_id_equal || FNAMECMP(si->sn_name, fname) == 0)) { + if (si->sn_name != NULL && FNAMECMP(si->sn_name, fname) == 0) { + // Found it! break; } } if (script_sctx.sc_sid == 0) { si = new_script_item((char *)vim_strsave(fname), &script_sctx.sc_sid); - if (file_id_ok) { - si->file_id_valid = true; - si->file_id = file_id; - } else { - si->file_id_valid = false; - } } if (ret_sctx != NULL) { *ret_sctx = script_sctx; diff --git a/src/nvim/ex_cmds2.h b/src/nvim/ex_cmds2.h index 74e52dfb4b..c463bfa5ab 100644 --- a/src/nvim/ex_cmds2.h +++ b/src/nvim/ex_cmds2.h @@ -15,12 +15,8 @@ #define CCGD_ALLBUF 8 // may write all buffers #define CCGD_EXCMD 16 // may suggest using ! -/// Also store the dev/ino, so that we don't have to stat() each -/// script when going through the list. typedef struct scriptitem_S { char_u *sn_name; - bool file_id_valid; - FileID file_id; bool sn_prof_on; ///< true when script is/was profiled bool sn_pr_force; ///< forceit: profile functions in this script proftime_T sn_pr_child; ///< time set when going into first child diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index d4fe55392e..2899e17039 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7899,9 +7899,11 @@ void do_exedit(exarg_T *eap, win_T *old_curwin) need_wait_return = false; msg_scroll = 0; redraw_all_later(NOT_VALID); + pending_exmode_active = true; normal_enter(false, true); + pending_exmode_active = false; RedrawingDisabled = rd; no_wait_return = nwr; msg_scroll = ms; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 7e22ed55cb..32977569c3 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -3224,7 +3224,7 @@ static void draw_cmdline(int start, int len) int u8cc[MAX_MCO]; int u8c = utfc_ptr2char_len(p, u8cc, start + len - i); mb_l = utfc_ptr2len_len(p, start + len - i); - if (arabic_char(u8c)) { + if (ARABIC_CHAR(u8c)) { do_arabicshape = true; break; } @@ -3260,7 +3260,7 @@ static void draw_cmdline(int start, int len) int u8cc[MAX_MCO]; int u8c = utfc_ptr2char_len(p, u8cc, start + len - i); mb_l = utfc_ptr2len_len(p, start + len - i); - if (arabic_char(u8c)) { + if (ARABIC_CHAR(u8c)) { int pc; int pc1 = 0; int nc = 0; diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 0f6eddef9d..00372d4f3d 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2475,6 +2475,11 @@ static int vgetorpeek(bool advance) } tc = c; + // return 0 in normal_check() + if (pending_exmode_active) { + exmode_active = true; + } + // no chars to block abbreviations for typebuf.tb_no_abbr_cnt = 0; diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 0281eebcee..01d4ab086e 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -636,6 +636,10 @@ EXTERN int motion_force INIT(=0); // motion force for pending operator // Ex Mode (Q) state EXTERN bool exmode_active INIT(= false); // true if Ex mode is active + +/// Flag set when normal_check() should return 0 when entering Ex mode. +EXTERN bool pending_exmode_active INIT(= false); + EXTERN bool ex_no_reprint INIT(=false); // No need to print after z or p. // 'inccommand' command preview state diff --git a/src/nvim/grid.c b/src/nvim/grid.c index 7d407bd3d1..1268f987e1 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -241,7 +241,7 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row, int col u8c = utfc_ptr2char(ptr, u8cc); } mbyte_cells = utf_char2cells(u8c); - if (p_arshape && !p_tbidi && arabic_char(u8c)) { + if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) { // Do Arabic shaping. if (len >= 0 && (int)(ptr - text) + mbyte_blen >= len) { // Past end of string to be displayed. diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 32e2d515e1..03d7cb1783 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1869,7 +1869,7 @@ static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, b schar_from_ascii(dest[0], *p); s->prev_c = u8c; } else { - if (p_arshape && !p_tbidi && arabic_char(u8c)) { + if (p_arshape && !p_tbidi && ARABIC_CHAR(u8c)) { // Do Arabic shaping. int pc, pc1, nc; int pcc[MAX_MCO]; @@ -3157,7 +3157,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc } } else if (mb_l == 0) { // at the NUL at end-of-line mb_l = 1; - } else if (p_arshape && !p_tbidi && arabic_char(mb_c)) { + } else if (p_arshape && !p_tbidi && ARABIC_CHAR(mb_c)) { // Do Arabic shaping. int pc, pc1, nc; int pcc[MAX_MCO]; diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index b0d872e392..d0a666a049 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -191,6 +191,9 @@ func RunTheTest(test) endtry endif + " Align Nvim defaults to Vim. + source setup.vim + " Clear any autocommands and put back the catch-all for SwapExists. au! au SwapExists * call HandleSwapExists() diff --git a/src/nvim/testdir/setup.vim b/src/nvim/testdir/setup.vim index dfcde37f62..f8db2ea120 100644 --- a/src/nvim/testdir/setup.vim +++ b/src/nvim/testdir/setup.vim @@ -1,39 +1,46 @@ -" Common preparations for running tests. - -" Only load this once. -if exists('s:did_load') - finish -endif -let s:did_load = 1 - " Align Nvim defaults to Vim. set backspace= +set complete=.,w,b,u,t,i +set directory& set directory^=. set fillchars=vert:\|,fold:- +set formatoptions=tcq set fsync set laststatus=1 set listchars=eol:$ set joinspaces -set nohidden nosmarttab noautoindent noautoread complete-=i noruler noshowcmd -set nrformats+=octal -set shortmess-=F +set nohidden nosmarttab noautoindent noautoread noruler noshowcmd +set nohlsearch noincsearch +set nrformats=bin,octal,hex +set shortmess=filnxtToOS set sidescroll=0 set tags=./tags,tags +set undodir& set undodir^=. set wildoptions= set startofline +set sessionoptions& set sessionoptions+=options +set viewoptions& set viewoptions+=options set switchbuf= -" Clear Nvim default mappings. -mapclear -mapclear! - " Make "Q" switch to Ex mode. " This does not work for all tests. nnoremap Q gQ +" Common preparations for running tests. + +" Only load this once. +if exists('s:did_load') + finish +endif +let s:did_load = 1 + +" Clear Nvim default mappings. +mapclear +mapclear! + " Prevent Nvim log from writing to stderr. let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log' diff --git a/src/nvim/testdir/test_arabic.vim b/src/nvim/testdir/test_arabic.vim index 450c6f98f5..272937387d 100644 --- a/src/nvim/testdir/test_arabic.vim +++ b/src/nvim/testdir/test_arabic.vim @@ -2,9 +2,8 @@ " NOTE: This just checks if the code works. If you know Arabic please add " functional tests that check the shaping works with real text. -if !has('arabic') - throw 'Skipped: arabic feature missing' -endif +source check.vim +CheckFeature arabic source view_util.vim @@ -563,3 +562,26 @@ func Test_shape_combination_isolated() set arabicshape& bwipe! endfunc + +" Test for entering arabic character in a search command +func Test_arabic_chars_in_search_cmd() + new + set arabic + call feedkeys("i\nsghl!\<C-^>vim\<C-^>", 'tx') + call cursor(1, 1) + call feedkeys("/^sghl!\<C-^>vim$\<C-^>\<CR>", 'tx') + call assert_equal([2, 1], [line('.'), col('.')]) + + " Try searching in left-to-right mode + set rightleftcmd= + call cursor(1, 1) + call feedkeys("/^sghl!\<C-^>vim$\<CR>", 'tx') + call assert_equal([2, 1], [line('.'), col('.')]) + + set rightleftcmd& + set rightleft& + set arabic& + bwipe! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_assert.vim b/src/nvim/testdir/test_assert.vim index 28c5948142..9d0d5b3e70 100644 --- a/src/nvim/testdir/test_assert.vim +++ b/src/nvim/testdir/test_assert.vim @@ -257,6 +257,26 @@ func Test_assert_with_msg() call remove(v:errors, 0) endfunc +func Test_mouse_position() + throw 'Skipped: Nvim does not have test_setmouse()' + let save_mouse = &mouse + set mouse=a + new + call setline(1, ['line one', 'line two']) + call assert_equal([0, 1, 1, 0], getpos('.')) + call test_setmouse(1, 5) + call feedkeys("\<LeftMouse>", "xt") + call assert_equal([0, 1, 5, 0], getpos('.')) + call test_setmouse(2, 20) + call feedkeys("\<LeftMouse>", "xt") + call assert_equal([0, 2, 8, 0], getpos('.')) + call test_setmouse(5, 1) + call feedkeys("\<LeftMouse>", "xt") + call assert_equal([0, 2, 1, 0], getpos('.')) + bwipe! + let &mouse = save_mouse +endfunc + " Must be last. func Test_zz_quit_detected() " Verify that if a test function ends Vim the test script detects this. diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index fcef57e47a..4b575bdfc3 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1720,7 +1720,7 @@ func Test_Cmd_Autocmds() au BufWriteCmd XtestE call extend(g:lines, getline(0, '$')) wall " will write other window to 'lines' call assert_equal(4, len(g:lines), g:lines) - call assert_equal("\tasdf", g:lines[2]) + call assert_equal("asdf", g:lines[2]) au! BufReadCmd au! BufWriteCmd diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index b00764e0dd..93b5e4d1cf 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -2,6 +2,7 @@ source check.vim source screendump.vim +source view_util.vim func Test_complete_tab() call writefile(['testfile'], 'Xtestfile') @@ -18,6 +19,11 @@ func Test_complete_list() " We can't see the output, but at least we check the code runs properly. call feedkeys(":e test\<C-D>\r", "tx") call assert_equal('test', expand('%:t')) + + " If a command doesn't support completion, then CTRL-D should be literally + " used. + call feedkeys(":chistory \<C-D>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"chistory \<C-D>", @:) endfunc func Test_complete_wildmenu() @@ -71,6 +77,11 @@ func Test_complete_wildmenu() cunmap <C-K> endif + " Test for canceling the wild menu by adding a character + redrawstatus + call feedkeys(":e Xdir1/\<Tab>x\<C-B>\"\<CR>", 'xt') + call assert_equal('"e Xdir1/Xdir2/x', @:) + " Completion using a relative path cd Xdir1/Xdir2 call feedkeys(":e ../\<Tab>\<Right>\<Down>\<C-A>\<C-B>\"\<CR>", 'tx') @@ -588,8 +599,17 @@ func Test_cmdline_paste() endtry call assert_equal("Xtestfile", bufname("%")) - " Use an invalid expression for <C-\>e - call assert_beeps('call feedkeys(":\<C-\>einvalid\<CR>", "tx")') + " Try to paste an invalid register using <C-R> + call feedkeys(":\"one\<C-R>\<C-X>two\<CR>", 'xt') + call assert_equal('"onetwo', @:) + + let @a = "xy\<C-H>z" + call feedkeys(":\"\<C-R>a\<CR>", 'xt') + call assert_equal('"xz', @:) + call feedkeys(":\"\<C-R>\<C-O>a\<CR>", 'xt') + call assert_equal("\"xy\<C-H>z", @:) + + call assert_beeps('call feedkeys(":\<C-R>=\<C-R>=\<Esc>", "xt")') bwipe! endfunc @@ -787,6 +807,149 @@ func Test_cmdline_complete_expression() unlet g:SomeVar endfunc +" Test for various command-line completion +func Test_cmdline_complete_various() + " completion for a command starting with a comment + call feedkeys(": :|\"\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\" :|\"\<C-A>", @:) + + " completion for a range followed by a comment + call feedkeys(":1,2\"\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"1,2\"\<C-A>", @:) + + " completion for :k command + call feedkeys(":ka\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"ka\<C-A>", @:) + + " completion for short version of the :s command + call feedkeys(":sI \<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"sI \<C-A>", @:) + + " completion for :write command + call mkdir('Xdir') + call writefile(['one'], 'Xdir/Xfile1') + let save_cwd = getcwd() + cd Xdir + call feedkeys(":w >> \<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"w >> Xfile1", @:) + call chdir(save_cwd) + call delete('Xdir', 'rf') + + " completion for :w ! and :r ! commands + call feedkeys(":w !invalid_xyz_cmd\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"w !invalid_xyz_cmd", @:) + call feedkeys(":r !invalid_xyz_cmd\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"r !invalid_xyz_cmd", @:) + + " completion for :>> and :<< commands + call feedkeys(":>>>\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\">>>\<C-A>", @:) + call feedkeys(":<<<\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"<<<\<C-A>", @:) + + " completion for command with +cmd argument + call feedkeys(":buffer +/pat Xabc\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"buffer +/pat Xabc", @:) + call feedkeys(":buffer +/pat\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"buffer +/pat\<C-A>", @:) + + " completion for a command with a trailing comment + call feedkeys(":ls \" comment\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"ls \" comment\<C-A>", @:) + + " completion for a command with a trailing command + call feedkeys(":ls | ls\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"ls | ls", @:) + + " completion for a command with an CTRL-V escaped argument + call feedkeys(":ls \<C-V>\<C-V>a\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"ls \<C-V>a\<C-A>", @:) + + " completion for a command that doesn't take additional arguments + call feedkeys(":all abc\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"all abc\<C-A>", @:) + + " completion for a command with a command modifier + call feedkeys(":topleft new\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"topleft new", @:) + + " completion for the :match command + call feedkeys(":match Search /pat/\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"match Search /pat/\<C-A>", @:) + + " completion for the :s command + call feedkeys(":s/from/to/g\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"s/from/to/g\<C-A>", @:) + + " completion for the :dlist command + call feedkeys(":dlist 10 /pat/ a\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"dlist 10 /pat/ a\<C-A>", @:) + + " completion for the :doautocmd command + call feedkeys(":doautocmd User MyCmd a.c\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"doautocmd User MyCmd a.c\<C-A>", @:) + + " completion for the :augroup command + augroup XTest + augroup END + call feedkeys(":augroup X\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"augroup XTest", @:) + augroup! XTest + + " completion for the :unlet command + call feedkeys(":unlet one two\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"unlet one two", @:) + + " completion for the :bdelete command + call feedkeys(":bdel a b c\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"bdel a b c", @:) + + " completion for the :mapclear command + call feedkeys(":mapclear \<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal("\"mapclear <buffer>", @:) + + " completion for user defined commands with menu names + menu Test.foo :ls<CR> + com -nargs=* -complete=menu MyCmd + call feedkeys(":MyCmd Te\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"MyCmd Test.', @:) + delcom MyCmd + unmenu Test + + " completion for user defined commands with mappings + mapclear + map <F3> :ls<CR> + com -nargs=* -complete=mapping MyCmd + call feedkeys(":MyCmd <F\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"MyCmd <F3>', @:) + mapclear + delcom MyCmd + + " completion for :set path= with multiple backslashes + call feedkeys(":set path=a\\\\\\ b\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"set path=a\\\ b', @:) + + " completion for :set dir= with a backslash + call feedkeys(":set dir=a\\ b\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"set dir=a\ b', @:) + + " completion for the :py3 commands + call feedkeys(":py3\<C-A>\<C-B>\"\<CR>", 'xt') + call assert_equal('"py3 py3do py3file', @:) + + " redir @" is not the start of a comment. So complete after that + call feedkeys(":redir @\" | cwin\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"redir @" | cwindow', @:) + + " completion after a backtick + call feedkeys(":e `a1b2c\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"e `a1b2c', @:) + + " completion for the expression register + call feedkeys(":\"\<C-R>=float2\t\"\<C-B>\"\<CR>", 'xt') + call assert_equal('"float2nr("', @=) +endfunc + func Test_cmdline_write_alternatefile() new call setline('.', ['one', 'two']) @@ -1226,6 +1389,164 @@ func Test_cmdline_expand_home() call delete('Xdir', 'rf') endfunc +" Test for using CTRL-\ CTRL-G in the command line to go back to normal mode +" or insert mode (when 'insertmode' is set) +func Test_cmdline_ctrl_g() + new + call setline(1, 'abc') + call cursor(1, 3) + " If command line is entered from insert mode, using C-\ C-G should back to + " insert mode + call feedkeys("i\<C-O>:\<C-\>\<C-G>xy", 'xt') + call assert_equal('abxyc', getline(1)) + call assert_equal(4, col('.')) + + " If command line is entered in 'insertmode', using C-\ C-G should back to + " 'insertmode' + " call feedkeys(":set im\<cr>\<C-L>:\<C-\>\<C-G>12\<C-L>:set noim\<cr>", 'xt') + " call assert_equal('ab12xyc', getline(1)) + close! +endfunc + +" Test for 'wildmode' +func Test_wildmode() + func T(a, c, p) + return "oneA\noneB\noneC" + endfunc + command -nargs=1 -complete=custom,T MyCmd + + func SaveScreenLine() + let g:Sline = Screenline(&lines - 1) + return '' + endfunc + cnoremap <expr> <F2> SaveScreenLine() + + set nowildmenu + set wildmode=full,list + let g:Sline = '' + call feedkeys(":MyCmd \t\t\<F2>\<C-B>\"\<CR>", 'xt') + call assert_equal('oneA oneB oneC', g:Sline) + call assert_equal('"MyCmd oneA', @:) + + set wildmode=longest,full + call feedkeys(":MyCmd o\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"MyCmd one', @:) + call feedkeys(":MyCmd o\t\t\t\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"MyCmd oneC', @:) + + set wildmode=longest + call feedkeys(":MyCmd one\t\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"MyCmd one', @:) + + set wildmode=list:longest + let g:Sline = '' + call feedkeys(":MyCmd \t\<F2>\<C-B>\"\<CR>", 'xt') + call assert_equal('oneA oneB oneC', g:Sline) + call assert_equal('"MyCmd one', @:) + + set wildmode="" + call feedkeys(":MyCmd \t\t\<C-B>\"\<CR>", 'xt') + call assert_equal('"MyCmd oneA', @:) + + " Test for wildmode=longest with 'fileignorecase' set + set wildmode=longest + set fileignorecase + argadd AA AAA AAAA + call feedkeys(":buffer \t\<C-B>\"\<CR>", 'xt') + call assert_equal('"buffer AA', @:) + set fileignorecase& + + " Test for listing files with wildmode=list + set wildmode=list + let g:Sline = '' + call feedkeys(":b A\t\t\<F2>\<C-B>\"\<CR>", 'xt') + call assert_equal('AA AAA AAAA', g:Sline) + call assert_equal('"b A', @:) + + %argdelete + delcommand MyCmd + delfunc T + delfunc SaveScreenLine + cunmap <F2> + set wildmode& + %bwipe! +endfunc + +" Test for interrupting the command-line completion +func Test_interrupt_compl() + func F(lead, cmdl, p) + if a:lead =~ 'tw' + call interrupt() + return + endif + return "one\ntwo\nthree" + endfunc + command -nargs=1 -complete=custom,F Tcmd + + set nowildmenu + set wildmode=full + let interrupted = 0 + try + call feedkeys(":Tcmd tw\<Tab>\<C-B>\"\<CR>", 'xt') + catch /^Vim:Interrupt$/ + let interrupted = 1 + endtry + call assert_equal(1, interrupted) + + delcommand Tcmd + delfunc F + set wildmode& +endfunc + +" Test for moving the cursor on the : command line +func Test_cmdline_edit() + let str = ":one two\<C-U>" + let str ..= "one two\<C-W>\<C-W>" + let str ..= "\<Left>five\<Right>" + let str ..= "\<Home>two " + let str ..= "\<C-Left>one " + let str ..= "\<C-Right> three" + let str ..= "\<End>\<S-Left>four " + let str ..= "\<S-Right> six" + let str ..= "\<C-B>\"\<C-E> seven\<CR>" + call feedkeys(str, 'xt') + call assert_equal("\"one two three four five six seven", @:) +endfunc + +" Test for moving the cursor on the / command line in 'rightleft' mode +func Test_cmdline_edit_rightleft() + CheckFeature rightleft + set rightleft + set rightleftcmd=search + let str = "/one two\<C-U>" + let str ..= "one two\<C-W>\<C-W>" + let str ..= "\<Right>five\<Left>" + let str ..= "\<Home>two " + let str ..= "\<C-Right>one " + let str ..= "\<C-Left> three" + let str ..= "\<End>\<S-Right>four " + let str ..= "\<S-Left> six" + let str ..= "\<C-B>\"\<C-E> seven\<CR>" + call assert_fails("call feedkeys(str, 'xt')", 'E486:') + call assert_equal("\"one two three four five six seven", @/) + set rightleftcmd& + set rightleft& +endfunc + +" Test for using <C-\>e in the command line to evaluate an expression +func Test_cmdline_expr() + " Evaluate an expression from the beginning of a command line + call feedkeys(":abc\<C-B>\<C-\>e\"\\\"hello\"\<CR>\<CR>", 'xt') + call assert_equal('"hello', @:) + + " Use an invalid expression for <C-\>e + call assert_beeps('call feedkeys(":\<C-\>einvalid\<CR>", "tx")') + + " Insert literal <CTRL-\> in the command line + call feedkeys(":\"e \<C-\>\<C-Y>\<CR>", 'xt') + call assert_equal("\"e \<C-\>\<C-Y>", @:) +endfunc + " Test for normal mode commands not supported in the cmd window func Test_cmdwin_blocked_commands() call assert_fails('call feedkeys("q:\<C-T>\<CR>", "xt")', 'E11:') diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim index c7dddf4164..55b230373f 100644 --- a/src/nvim/testdir/test_command_count.vim +++ b/src/nvim/testdir/test_command_count.vim @@ -33,7 +33,7 @@ func Test_command_count_0() delcommand RangeBuffers delcommand RangeBuffersAll - set nohidden + set hidden& set swapfile& endfunc diff --git a/src/nvim/testdir/test_cscope.vim b/src/nvim/testdir/test_cscope.vim index faf37485cd..76ea35fa10 100644 --- a/src/nvim/testdir/test_cscope.vim +++ b/src/nvim/testdir/test_cscope.vim @@ -1,7 +1,11 @@ " Test for cscope commands. -if !has('cscope') || !executable('cscope') || !has('quickfix') - finish +source check.vim +CheckFeature cscope +CheckFeature quickfix + +if !executable('cscope') + throw 'Skipped: cscope program missing' endif func CscopeSetupOrClean(setup) diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index be9a77ee75..8c20c647f1 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -540,7 +540,7 @@ func Test_diffopt_hiddenoff() bwipe! bwipe! - set nohidden diffopt& + set hidden& diffopt& endfunc func Test_diffoff_hidden() @@ -577,7 +577,7 @@ func Test_diffoff_hidden() bwipe! bwipe! - set nohidden diffopt& + set hidden& diffopt& endfunc func Test_setting_cursor() diff --git a/src/nvim/testdir/test_ex_mode.vim b/src/nvim/testdir/test_ex_mode.vim index c3f7fb45f4..cff78620d1 100644 --- a/src/nvim/testdir/test_ex_mode.vim +++ b/src/nvim/testdir/test_ex_mode.vim @@ -64,6 +64,23 @@ func Test_ex_mode() let &encoding = encoding_save endfunc +" Test for :g/pat/visual to run vi commands in Ex mode +" This used to hang Vim before 8.2.0274. +func Test_Ex_global() + new + call setline(1, ['', 'foo', 'bar', 'foo', 'bar', 'foo']) + call feedkeys("Q\<bs>g/bar/visual\<CR>$rxQ$ryQvisual\<CR>j", "xt") + call assert_equal('bax', getline(3)) + call assert_equal('bay', getline(5)) + bwipe! +endfunc + +" In Ex-mode, a backslash escapes a newline +func Test_Ex_escape_enter() + call feedkeys("gQlet l = \"a\\\<kEnter>b\"\<cr>vi\<cr>", 'xt') + call assert_equal("a\rb", l) +endfunc + func Test_ex_mode_errors() " Not allowed to enter ex mode when text is locked au InsertCharPre <buffer> normal! gQ<CR> diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index b3052abb24..df2cf97633 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -147,7 +147,6 @@ endfunc " Test for the :insert command func Test_insert_cmd() - set noautoindent " test assumes noautoindent, but it's on by default in Nvim new call setline(1, [' L1']) call feedkeys(":insert\<CR> L2\<CR> L3\<CR>.\<CR>", 'xt') @@ -197,7 +196,6 @@ endfunc " Test for the :change command func Test_change_cmd() - set noautoindent " test assumes noautoindent, but it's on by default in Nvim new call setline(1, [' L1', 'L2', 'L3']) call feedkeys(":change\<CR> L4\<CR> L5\<CR>.\<CR>", 'xt') diff --git a/src/nvim/testdir/test_filetype_lua.vim b/src/nvim/testdir/test_filetype_lua.vim deleted file mode 100644 index d9c0dcba9c..0000000000 --- a/src/nvim/testdir/test_filetype_lua.vim +++ /dev/null @@ -1,3 +0,0 @@ -let g:do_filetype_lua = 1 -let g:did_load_filetypes = 0 -source test_filetype.vim diff --git a/src/nvim/testdir/test_float_func.vim b/src/nvim/testdir/test_float_func.vim index 1e0c75c49d..902a011a9d 100644 --- a/src/nvim/testdir/test_float_func.vim +++ b/src/nvim/testdir/test_float_func.vim @@ -1,8 +1,7 @@ " test float functions -if !has('float') - finish -end +source check.vim +CheckFeature float func Test_abs() call assert_equal('1.23', string(abs(1.23))) diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 2391b4a485..3feb4d5f7f 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1218,6 +1218,16 @@ func Test_input_func() call assert_fails("call input('F:', '', [])", 'E730:') endfunc +" Test for the inputdialog() function +func Test_inputdialog() + CheckNotGui + + call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<CR>", 'xt') + call assert_equal('xx', v) + call feedkeys(":let v=inputdialog('Q:', 'xx', 'yy')\<CR>\<Esc>", 'xt') + call assert_equal('yy', v) +endfunc + " Test for inputlist() func Test_inputlist() call feedkeys(":let c = inputlist(['Select color:', '1. red', '2. green', '3. blue'])\<cr>1\<cr>", 'tx') diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim index 7cb9daf59a..403fc24391 100644 --- a/src/nvim/testdir/test_gf.vim +++ b/src/nvim/testdir/test_gf.vim @@ -137,7 +137,7 @@ func Test_gf_visual() bwipe! call delete('Xtest_gf_visual') - set nohidden + set hidden& endfunc func Test_gf_error() diff --git a/src/nvim/testdir/test_history.vim b/src/nvim/testdir/test_history.vim index 3b3dc17f44..96006ac7e7 100644 --- a/src/nvim/testdir/test_history.vim +++ b/src/nvim/testdir/test_history.vim @@ -1,8 +1,7 @@ " Tests for the history functions -if !has('cmdline_hist') - finish -endif +source check.vim +CheckFeature cmdline_hist set history=7 @@ -71,6 +70,14 @@ function History_Tests(hist) call assert_equal('', histget(a:hist, i)) call assert_equal('', histget(a:hist, i - 7 - 1)) endfor + + " Test for freeing an entry at the beginning of the history list + for i in range(1, 4) + call histadd(a:hist, 'text_' . i) + endfor + call histdel(a:hist, 1) + call assert_equal('', histget(a:hist, 1)) + call assert_equal('text_4', histget(a:hist, 4)) endfunction function Test_History() @@ -116,14 +123,14 @@ endfunc func Test_history_size() let save_histsz = &history call histdel(':') - set history=5 + set history=10 for i in range(1, 5) call histadd(':', 'cmd' .. i) endfor call assert_equal(5, histnr(':')) call assert_equal('cmd5', histget(':', -1)) - set history=10 + set history=15 for i in range(6, 10) call histadd(':', 'cmd' .. i) endfor @@ -138,6 +145,15 @@ func Test_history_size() call assert_equal('cmd7', histget(':', 7)) call assert_equal('abc', histget(':', -1)) + " This test works only when the language is English + if v:lang == "C" || v:lang =~ '^[Ee]n' + set history=0 + redir => v + call feedkeys(":history\<CR>", 'xt') + redir END + call assert_equal(["'history' option is zero"], split(v, "\n")) + endif + let &history=save_histsz endfunc @@ -159,4 +175,12 @@ func Test_history_search() delfunc SavePat endfunc +" Test for making sure the key value is not stored in history +func Test_history_crypt_key() + CheckFeature cryptv + call feedkeys(":set bs=2 key=abc ts=8\<CR>", 'xt') + call assert_equal('set bs=2 key= ts=8', histget(':')) + set key& bs& ts& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index c8104c6b73..e963f308a2 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -95,7 +95,7 @@ func Test_ins_complete() call delete('Xtest11.one') call delete('Xtest11.two') call delete('Xtestdata') - set cpt& cot& def& tags& tagbsearch& nohidden + set cpt& cot& def& tags& tagbsearch& hidden& cd .. call delete('Xdir', 'rf') endfunc diff --git a/src/nvim/testdir/test_join.vim b/src/nvim/testdir/test_join.vim index ecb969d10a..ac6ef8f29f 100644 --- a/src/nvim/testdir/test_join.vim +++ b/src/nvim/testdir/test_join.vim @@ -51,7 +51,7 @@ func Test_join_marks() /^This line/;'}-join call assert_equal([0, 4, 11, 0], getpos("'[")) - call assert_equal([0, 4, 66, 0], getpos("']")) + call assert_equal([0, 4, 67, 0], getpos("']")) enew! endfunc diff --git a/src/nvim/testdir/test_langmap.vim b/src/nvim/testdir/test_langmap.vim index eca8a95564..4f831aa40b 100644 --- a/src/nvim/testdir/test_langmap.vim +++ b/src/nvim/testdir/test_langmap.vim @@ -1,5 +1,8 @@ " tests for 'langmap' +source check.vim +CheckFeature langmap + func Test_langmap() new set langmap=}l,^x,%v diff --git a/src/nvim/testdir/test_legacy_filetype.vim b/src/nvim/testdir/test_legacy_filetype.vim new file mode 100644 index 0000000000..772faaadb0 --- /dev/null +++ b/src/nvim/testdir/test_legacy_filetype.vim @@ -0,0 +1,4 @@ +let g:do_legacy_filetype = 1 +filetype on + +source test_filetype.vim diff --git a/src/nvim/testdir/test_listlbr.vim b/src/nvim/testdir/test_listlbr.vim index 2fda12d8b4..affa0f96fa 100644 --- a/src/nvim/testdir/test_listlbr.vim +++ b/src/nvim/testdir/test_listlbr.vim @@ -2,9 +2,9 @@ scriptencoding latin1 -if !exists("+linebreak") || !has("conceal") - finish -endif +source check.vim +CheckOption linebreak +CheckFeature conceal source view_util.vim diff --git a/src/nvim/testdir/test_listlbr_utf8.vim b/src/nvim/testdir/test_listlbr_utf8.vim index 1f100d6244..df1ed78119 100644 --- a/src/nvim/testdir/test_listlbr_utf8.vim +++ b/src/nvim/testdir/test_listlbr_utf8.vim @@ -3,9 +3,10 @@ set encoding=utf-8 scriptencoding utf-8 -if !exists("+linebreak") || !has("conceal") || !has("signs") - finish -endif +source check.vim +CheckOption linebreak +CheckFeature conceal +CheckFeature signs source view_util.vim diff --git a/src/nvim/testdir/test_makeencoding.vim b/src/nvim/testdir/test_makeencoding.vim index 2b346e0720..c53c07d991 100644 --- a/src/nvim/testdir/test_makeencoding.vim +++ b/src/nvim/testdir/test_makeencoding.vim @@ -4,8 +4,7 @@ source shared.vim let s:python = PythonProg() if s:python == '' - " Can't run this test. - finish + throw 'Skipped: python program missing' endif let s:script = 'test_makeencoding.py' diff --git a/src/nvim/testdir/test_matchadd_conceal_utf8.vim b/src/nvim/testdir/test_matchadd_conceal_utf8.vim index 1d0c740734..f33c7f694c 100644 --- a/src/nvim/testdir/test_matchadd_conceal_utf8.vim +++ b/src/nvim/testdir/test_matchadd_conceal_utf8.vim @@ -1,7 +1,7 @@ " Test for matchadd() and conceal feature using utf-8. -if !has('conceal') - finish -endif + +source check.vim +CheckFeature conceal func s:screenline(lnum) abort let line = [] diff --git a/src/nvim/testdir/test_menu.vim b/src/nvim/testdir/test_menu.vim index a2ef6e5c67..4af75be514 100644 --- a/src/nvim/testdir/test_menu.vim +++ b/src/nvim/testdir/test_menu.vim @@ -1,8 +1,7 @@ " Test that the system menu can be loaded. -if !has('menu') - finish -endif +source check.vim +CheckFeature menu func Test_load_menu() try diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index cf90e416c4..8ec408e62e 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -2,9 +2,8 @@ scriptencoding latin1 -if !has('mksession') - finish -endif +source check.vim +CheckFeature mksession source shared.vim source term_util.vim @@ -43,9 +42,9 @@ func Test_mksession() \ ' four leadinG spaces', \ 'two consecutive tabs', \ 'two tabs in one line', - \ 'one ä multibyteCharacter', - \ 'aä Ä two multiByte characters', - \ 'Aäöü three mulTibyte characters', + \ 'one ä multibyteCharacter', + \ 'aä Ä two multiByte characters', + \ 'Aäöü three mulTibyte characters', \ 'short line', \ ]) let tmpfile = 'Xtemp' diff --git a/src/nvim/testdir/test_mksession_utf8.vim b/src/nvim/testdir/test_mksession_utf8.vim index 722fd28beb..4e593cc21a 100644 --- a/src/nvim/testdir/test_mksession_utf8.vim +++ b/src/nvim/testdir/test_mksession_utf8.vim @@ -3,9 +3,8 @@ set encoding=utf-8 scriptencoding utf-8 -if !has('mksession') - finish -endif +source check.vim +CheckFeature mksession func Test_mksession_utf8() tabnew diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 2a3d977254..7b6cfa6bb4 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -630,7 +630,7 @@ func Test_copy_winopt() call assert_equal(4,&numberwidth) bw! - set nohidden + set hidden& endfunc func Test_shortmess_F() @@ -752,6 +752,29 @@ func Test_shellquote() call assert_match(': "#echo Hello#"', v) endfunc +" Test for the 'rightleftcmd' option +func Test_rightleftcmd() + CheckFeature rightleft + set rightleft + set rightleftcmd + + let g:l = [] + func AddPos() + call add(g:l, screencol()) + return '' + endfunc + cmap <expr> <F2> AddPos() + + call feedkeys("/\<F2>abc\<Left>\<F2>\<Right>\<Right>\<F2>" .. + \ "\<Left>\<F2>\<Esc>", 'xt') + call assert_equal([&co - 1, &co - 4, &co - 2, &co - 3], g:l) + + cunmap <F2> + unlet g:l + set rightleftcmd& + set rightleft& +endfunc + " Test for setting option values using v:false and v:true func Test_opt_boolean() set number& diff --git a/src/nvim/testdir/test_perl.vim b/src/nvim/testdir/test_perl.vim index b911a982f9..558d0a5d6b 100644 --- a/src/nvim/testdir/test_perl.vim +++ b/src/nvim/testdir/test_perl.vim @@ -1,8 +1,8 @@ " Tests for Perl interface -if !has('perl') || has('win32') - finish -endif +source check.vim +CheckFeature perl +CheckNotMSWindows " FIXME: RunTest don't see any error when Perl abort... perl $SIG{__WARN__} = sub { die "Unexpected warnings from perl: @_" }; diff --git a/src/nvim/testdir/test_python2.vim b/src/nvim/testdir/test_python2.vim index ae8bc57c7f..745b7da086 100644 --- a/src/nvim/testdir/test_python2.vim +++ b/src/nvim/testdir/test_python2.vim @@ -1,9 +1,8 @@ " Test for python 2 commands. " TODO: move tests from test86.in here. -if !has('python') - finish -endif +source check.vim +CheckFeature python func Test_pydo() " Check deleting lines does not trigger ml_get error. diff --git a/src/nvim/testdir/test_python3.vim b/src/nvim/testdir/test_python3.vim index f6a1942e24..69f5f6dcc0 100644 --- a/src/nvim/testdir/test_python3.vim +++ b/src/nvim/testdir/test_python3.vim @@ -1,9 +1,8 @@ " Test for python 3 commands. " TODO: move tests from test87.in here. -if !has('python3') - finish -endif +source check.vim +CheckFeature python3 func Test_py3do() " Check deleting lines does not trigger an ml_get error. diff --git a/src/nvim/testdir/test_pyx2.vim b/src/nvim/testdir/test_pyx2.vim index 6a8ebf3da0..eee825fa9b 100644 --- a/src/nvim/testdir/test_pyx2.vim +++ b/src/nvim/testdir/test_pyx2.vim @@ -1,8 +1,7 @@ " Test for pyx* commands and functions with Python 2. -if !has('python') - finish -endif +source check.vim +CheckFeature python set pyx=2 let s:py2pattern = '^2\.[0-7]\.\d\+' diff --git a/src/nvim/testdir/test_pyx3.vim b/src/nvim/testdir/test_pyx3.vim index 2044af3abe..db39f5134a 100644 --- a/src/nvim/testdir/test_pyx3.vim +++ b/src/nvim/testdir/test_pyx3.vim @@ -1,9 +1,8 @@ " Test for pyx* commands and functions with Python 3. set pyx=3 -if !has('python3') - finish -endif +source check.vim +CheckFeature python3 let s:py2pattern = '^2\.[0-7]\.\d\+' let s:py3pattern = '^3\.\d\+\.\d\+' diff --git a/src/nvim/testdir/test_quotestar.vim b/src/nvim/testdir/test_quotestar.vim index 6e6f91362b..93865869fa 100644 --- a/src/nvim/testdir/test_quotestar.vim +++ b/src/nvim/testdir/test_quotestar.vim @@ -1,10 +1,9 @@ " *-register (quotestar) tests -if !has('clipboard') - finish -endif - source shared.vim +source check.vim + +CheckFeature clipboard_working func Do_test_quotestar_for_macunix() if empty(exepath('pbcopy')) || empty(exepath('pbpaste')) diff --git a/src/nvim/testdir/test_reltime.vim b/src/nvim/testdir/test_reltime.vim index 37b9e783c6..b381f1ddbb 100644 --- a/src/nvim/testdir/test_reltime.vim +++ b/src/nvim/testdir/test_reltime.vim @@ -1,8 +1,8 @@ " Tests for reltime() -if !has('reltime') || !has('float') - finish -endif +source check.vim +CheckFeature reltime +CheckFeature float func Test_reltime() let now = reltime() diff --git a/src/nvim/testdir/test_sha256.vim b/src/nvim/testdir/test_sha256.vim index 76d1306836..f6f430b04e 100644 --- a/src/nvim/testdir/test_sha256.vim +++ b/src/nvim/testdir/test_sha256.vim @@ -1,8 +1,8 @@ " Tests for the sha256() function. -if !has('cryptv') || !exists('*sha256') - finish -endif +source check.vim +CheckFeature cryptv +CheckFunction sha256 function Test_sha256() " test for empty string: diff --git a/src/nvim/testdir/test_source.vim b/src/nvim/testdir/test_source.vim index 09baec0b7d..b8fe8422b3 100644 --- a/src/nvim/testdir/test_source.vim +++ b/src/nvim/testdir/test_source.vim @@ -46,3 +46,14 @@ func Test_source_sandbox() bwipe! call delete('Xsourcehello') endfunc + +" When deleting a file and immediately creating a new one the inode may be +" recycled. Vim should not recognize it as the same script. +func Test_different_script() + call writefile(['let s:var = "asdf"'], 'XoneScript') + source XoneScript + call delete('XoneScript') + call writefile(['let g:var = s:var'], 'XtwoScript') + call assert_fails('source XtwoScript', 'E121:') + call delete('XtwoScript') +endfunc diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim index 5099818384..58f0760f48 100644 --- a/src/nvim/testdir/test_spell.vim +++ b/src/nvim/testdir/test_spell.vim @@ -2,9 +2,7 @@ " Note: this file uses latin1 encoding, but is used with utf-8 encoding. source check.vim -if !has('spell') - finish -endif +CheckFeature spell source screendump.vim diff --git a/src/nvim/testdir/test_spellfile.vim b/src/nvim/testdir/test_spellfile.vim index f1331a9f2a..b028e9d969 100644 --- a/src/nvim/testdir/test_spellfile.vim +++ b/src/nvim/testdir/test_spellfile.vim @@ -525,6 +525,13 @@ func Test_mkspell() call assert_fails('mkspell! Xtest.spl Xtest.dic', 'E17:') call delete('Xtest.spl', 'rf') + " can't write the .spl file as its directory does not exist + call writefile([], 'Xtest.aff') + call writefile([], 'Xtest.dic') + call assert_fails('mkspell DOES_NOT_EXIT/Xtest.spl Xtest.dic', 'E484:') + call delete('Xtest.aff') + call delete('Xtest.dic') + call assert_fails('mkspell en en_US abc_xyz', 'E755:') endfunc @@ -616,6 +623,11 @@ func Test_aff_file_format_error() let output = execute('mkspell! Xtest.spl Xtest') call assert_match('Wrong CHECKCOMPOUNDPATTERN value in Xtest.aff line 1: 0', output) + " Both compounding and NOBREAK specified + call writefile(['COMPOUNDFLAG c', 'NOBREAK'], 'Xtest.aff') + let output = execute('mkspell! Xtest.spl Xtest') + call assert_match('Warning: both compounding and NOBREAK specified', output) + " Duplicate affix entry in an affix file call writefile(['PFX L Y 1', 'PFX L 0 re x', 'PFX L Y 1', 'PFX L 0 re x'], \ 'Xtest.aff') @@ -733,36 +745,54 @@ func Test_spell_add_word() %bw! endfunc -" When 'spellfile' is not set, adding a new good word will automatically set -" the 'spellfile' -func Test_init_spellfile() - let save_rtp = &rtp - let save_encoding = &encoding - call mkdir('Xrtp/spell', 'p') - call writefile(['vim'], 'Xrtp/spell/Xtest.dic') - silent mkspell Xrtp/spell/Xtest.utf-8.spl Xrtp/spell/Xtest.dic - set runtimepath=./Xrtp - set spelllang=Xtest +func Test_spellfile_verbose() + call writefile(['1', 'one'], 'XtestVerbose.dic') + call writefile([], 'XtestVerbose.aff') + mkspell! XtestVerbose-utf8.spl XtestVerbose set spell - silent spellgood abc - call assert_equal('./Xrtp/spell/Xtest.utf-8.add', &spellfile) - call assert_equal(['abc'], readfile('Xrtp/spell/Xtest.utf-8.add')) - call assert_true(filereadable('Xrtp/spell/Xtest.utf-8.spl')) - set spell& spelllang& spellfile& - call delete('Xrtp', 'rf') - let &encoding = save_encoding - let &rtp = save_rtp - %bw! + + " First time: the spl file should be read. + let a = execute('3verbose set spelllang=XtestVerbose-utf8.spl') + call assert_match('Reading spell file "XtestVerbose-utf8.spl"', a) + + " Second time time: the spl file should not be read (already read). + let a = execute('3verbose set spelllang=XtestVerbose-utf8.spl') + call assert_notmatch('Reading spell file "XtestVerbose-utf8.spl"', a) + + set spell& spelllang& + call delete('XtestVerbose.dic') + call delete('XtestVerbose.aff') + call delete('XtestVerbose-utf8.spl') endfunc -" Test for the 'mkspellmem' option -func Test_mkspellmem_opt() - call assert_fails('set mkspellmem=1000', 'E474:') - call assert_fails('set mkspellmem=1000,', 'E474:') - call assert_fails('set mkspellmem=1000,50', 'E474:') - call assert_fails('set mkspellmem=1000,50,', 'E474:') - call assert_fails('set mkspellmem=1000,50,10,', 'E474:') - call assert_fails('set mkspellmem=1000,50,0', 'E474:') +" Test NOBREAK (see :help spell-NOBREAK) +func Test_NOBREAK() + call writefile(['3', 'one', 'two', 'three' ], 'XtestNOBREAK.dic') + call writefile(['NOBREAK' ], 'XtestNOBREAK.aff') + + mkspell! XtestNOBREAK-utf8.spl XtestNOBREAK + set spell spelllang=XtestNOBREAK-utf8.spl + + call assert_equal(['', ''], spellbadword('One two three onetwo onetwothree threetwoone')) + + call assert_equal(['x', 'bad'], spellbadword('x')) + call assert_equal(['y', 'bad'], spellbadword('yone')) + call assert_equal(['z', 'bad'], spellbadword('onez')) + call assert_equal(['zero', 'bad'], spellbadword('Onetwozerothree')) + + new + call setline(1, 'Onetwwothree') + norm! fw1z= + call assert_equal('Onetwothree', getline(1)) + call setline(1, 'Onetwothre') + norm! fh1z= + call assert_equal('Onetwothree', getline(1)) + + bw! + set spell& spelllang& + call delete('XtestNOBREAK.dic') + call delete('XtestNOBREAK.aff') + call delete('XtestNOBREAK-utf8.spl') endfunc " Test CHECKCOMPOUNDPATTERN (see :help spell-CHECKCOMPOUNDPATTERN) @@ -777,7 +807,7 @@ func Test_spellfile_CHECKCOMPOUNDPATTERN() \ 'CHECKCOMPOUNDPATTERN wo on', \ 'COMPOUNDFLAG c'], 'XtestCHECKCOMPOUNDPATTERN.aff') - let output = execute('mkspell! XtestCHECKCOMPOUNDPATTERN-utf8.spl XtestCHECKCOMPOUNDPATTERN') + mkspell! XtestCHECKCOMPOUNDPATTERN-utf8.spl XtestCHECKCOMPOUNDPATTERN set spell spelllang=XtestCHECKCOMPOUNDPATTERN-utf8.spl " Check valid words with and without valid compounds. @@ -862,7 +892,7 @@ func Test_spellfile_COMMON() \ 'ted'], 'XtestCOMMON.dic') call writefile(['COMMON the and'], 'XtestCOMMON.aff') - let output = execute('mkspell! XtestCOMMON-utf8.spl XtestCOMMON') + mkspell! XtestCOMMON-utf8.spl XtestCOMMON set spell spelllang=XtestCOMMON-utf8.spl " COMMON words 'and' and 'the' should be the top suggestions. @@ -877,4 +907,121 @@ func Test_spellfile_COMMON() call delete('XtestCOMMON-utf8.spl') endfunc +" Test CIRCUMFIX (see: :help spell-CIRCUMFIX) +func Test_spellfile_CIRCUMFIX() + " Example taken verbatim from https://github.com/hunspell/hunspell/tree/master/tests + call writefile(['1', + \ 'nagy/C po:adj'], 'XtestCIRCUMFIX.dic') + call writefile(['# circumfixes: ~ obligate prefix/suffix combinations', + \ '# superlative in Hungarian: leg- (prefix) AND -bb (suffix)', + \ '', + \ 'CIRCUMFIX X', + \ '', + \ 'PFX A Y 1', + \ 'PFX A 0 leg/X .', + \ '', + \ 'PFX B Y 1', + \ 'PFX B 0 legesleg/X .', + \ '', + \ 'SFX C Y 3', + \ 'SFX C 0 obb . is:COMPARATIVE', + \ 'SFX C 0 obb/AX . is:SUPERLATIVE', + \ 'SFX C 0 obb/BX . is:SUPERSUPERLATIVE'], 'XtestCIRCUMFIX.aff') + + mkspell! XtestCIRCUMFIX-utf8.spl XtestCIRCUMFIX + set spell spelllang=XtestCIRCUMFIX-utf8.spl + + " From https://catalog.ldc.upenn.edu/docs/LDC2008T01/acta04.pdf: + " Hungarian English + " --------- ------- + " nagy great + " nagyobb greater + " legnagyobb greatest + " legeslegnagyob most greatest + call assert_equal(['', ''], spellbadword('nagy nagyobb legnagyobb legeslegnagyobb')) + + for badword in ['legnagy', 'legeslegnagy', 'legobb', 'legeslegobb'] + call assert_equal([badword, 'bad'], spellbadword(badword)) + endfor + + set spell& spelllang& + call delete('XtestCIRCUMFIX.dic') + call delete('XtestCIRCUMFIX.aff') + call delete('XtestCIRCUMFIX-utf8.spl') +endfunc + +" Test SFX that strips/chops characters +func Test_spellfile_SFX_strip() + " Simplified conjugation of Italian verbs ending in -are (first conjugation). + call writefile(['SFX A Y 4', + \ 'SFX A are iamo [^icg]are', + \ 'SFX A are hiamo [cg]are', + \ 'SFX A re mo iare', + \ 'SFX A re vamo are'], + \ 'XtestSFX.aff') + " Examples of Italian verbs: + " - cantare = to sing + " - cercare = to search + " - odiare = to hate + call writefile(['3', 'cantare/A', 'cercare/A', 'odiare/A'], 'XtestSFX.dic') + + mkspell! XtestSFX-utf8.spl XtestSFX + set spell spelllang=XtestSFX-utf8.spl + + " To sing, we're singing, we were singing. + call assert_equal(['', ''], spellbadword('cantare cantiamo cantavamo')) + + " To search, we're searching, we were searching. + call assert_equal(['', ''], spellbadword('cercare cerchiamo cercavamo')) + + " To hate, we hate, we were hating. + call assert_equal(['', ''], spellbadword('odiare odiamo odiavamo')) + + for badword in ['canthiamo', 'cerciamo', 'cantarevamo', 'odiiamo'] + call assert_equal([badword, 'bad'], spellbadword(badword)) + endfor + + call assert_equal(['cantiamo'], spellsuggest('canthiamo', 1)) + call assert_equal(['cerchiamo'], spellsuggest('cerciamo', 1)) + call assert_equal(['cantavamo'], spellsuggest('cantarevamo', 1)) + call assert_equal(['odiamo'], spellsuggest('odiiamo', 1)) + + set spell& spelllang& + call delete('XtestSFX.dic') + call delete('XtestSFX.aff') + call delete('XtestSFX-utf8.spl') +endfunc + +" When 'spellfile' is not set, adding a new good word will automatically set +" the 'spellfile' +func Test_init_spellfile() + let save_rtp = &rtp + let save_encoding = &encoding + call mkdir('Xrtp/spell', 'p') + call writefile(['vim'], 'Xrtp/spell/Xtest.dic') + silent mkspell Xrtp/spell/Xtest.utf-8.spl Xrtp/spell/Xtest.dic + set runtimepath=./Xrtp + set spelllang=Xtest + set spell + silent spellgood abc + call assert_equal('./Xrtp/spell/Xtest.utf-8.add', &spellfile) + call assert_equal(['abc'], readfile('Xrtp/spell/Xtest.utf-8.add')) + call assert_true(filereadable('Xrtp/spell/Xtest.utf-8.spl')) + set spell& spelllang& spellfile& + call delete('Xrtp', 'rf') + let &encoding = save_encoding + let &rtp = save_rtp + %bw! +endfunc + +" Test for the 'mkspellmem' option +func Test_mkspellmem_opt() + call assert_fails('set mkspellmem=1000', 'E474:') + call assert_fails('set mkspellmem=1000,', 'E474:') + call assert_fails('set mkspellmem=1000,50', 'E474:') + call assert_fails('set mkspellmem=1000,50,', 'E474:') + call assert_fails('set mkspellmem=1000,50,10,', 'E474:') + call assert_fails('set mkspellmem=1000,50,0', 'E474:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_statusline.vim b/src/nvim/testdir/test_statusline.vim index 8579457d9f..f36ff83fdf 100644 --- a/src/nvim/testdir/test_statusline.vim +++ b/src/nvim/testdir/test_statusline.vim @@ -9,6 +9,10 @@ source view_util.vim source check.vim source term_util.vim +func SetUp() + set laststatus=2 +endfunc + func s:get_statusline() return ScreenLines(&lines - 1, &columns)[0] endfunc diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 4d1a468e30..7ba0149971 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -1,5 +1,8 @@ " Test for syntax and syntax iskeyword option +source check.vim +CheckFeature syntax + source view_util.vim source screendump.vim diff --git a/src/nvim/testdir/test_taglist.vim b/src/nvim/testdir/test_taglist.vim index e11815ff33..be46773256 100644 --- a/src/nvim/testdir/test_taglist.vim +++ b/src/nvim/testdir/test_taglist.vim @@ -1,5 +1,7 @@ " test taglist(), tagfiles() functions and :tags command +source view_util.vim + func Test_taglist() call writefile([ \ "FFoo\tXfoo\t1", @@ -222,3 +224,21 @@ func Test_format_error() set tags& call delete('Xtags') endfunc + +" Test for :tag command completion with 'wildoptions' set to 'tagfile' +func Test_tag_complete_wildoptions() + call writefile(["foo\ta.c\t10;\"\tf", "bar\tb.c\t20;\"\td"], 'Xtags') + set tags=Xtags + set wildoptions=tagfile + + call feedkeys(":tag \<C-D>\<C-R>=Screenline(&lines - 1)\<CR> : " + \ .. "\<C-R>=Screenline(&lines - 2)\<CR>\<C-B>\"\<CR>", 'xt') + + call assert_equal('"tag bar d b.c : foo f a.c', @:) + + call delete('Xtags') + set wildoptions& + set tags& +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_textobjects.vim b/src/nvim/testdir/test_textobjects.vim index 81f2fea026..210aba19a9 100644 --- a/src/nvim/testdir/test_textobjects.vim +++ b/src/nvim/testdir/test_textobjects.vim @@ -1,8 +1,7 @@ " Test for textobjects -if !has('textobjects') - finish -endif +source check.vim +CheckFeature textobjects func CpoM(line, useM, expected) new diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index 5c67efb831..e5b4bc23e8 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -1,8 +1,7 @@ " Test for timers -if !has('timers') - finish -endif +source check.vim +CheckFeature timers source shared.vim source term_util.vim @@ -317,8 +316,8 @@ endfunc " Test that the garbage collector isn't triggered if a timer callback invokes " vgetc(). func Test_nocatch_garbage_collect() - CheckFunction test_garbagecollect_soon - CheckFunction test_override + " skipped: Nvim does not support test_garbagecollect_soon(), test_override() + return " 'uptimetime. must be bigger than the timer timeout set ut=200 call test_garbagecollect_soon() diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index 015a16705e..e37fe43b22 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -313,7 +313,7 @@ func CustomComplete(A, L, P) endfunc func CustomCompleteList(A, L, P) - return [ "Monday", "Tuesday", "Wednesday" ] + return [ "Monday", "Tuesday", "Wednesday", {}] endfunc func Test_CmdCompletion() diff --git a/src/nvim/testdir/test_vartabs.vim b/src/nvim/testdir/test_vartabs.vim index 6af199a512..68fe15ff93 100644 --- a/src/nvim/testdir/test_vartabs.vim +++ b/src/nvim/testdir/test_vartabs.vim @@ -1,8 +1,7 @@ " Test for variable tabstops -if !has("vartabs") - finish -endif +source check.vim +CheckFeature vartabs source view_util.vim diff --git a/src/nvim/testdir/test_window_cmd.vim b/src/nvim/testdir/test_window_cmd.vim index 0fe5fc59eb..d295e520c7 100644 --- a/src/nvim/testdir/test_window_cmd.vim +++ b/src/nvim/testdir/test_window_cmd.vim @@ -612,7 +612,7 @@ func Test_window_prevwin() " reset q call delete('tmp.txt') - set nohidden autoread&vim + set hidden&vim autoread&vim delfunc Fun_RenewFile endfunc diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 20cf1ed586..e2289eb9ce 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -1642,6 +1642,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col bool xterm = terminfo_is_term_family(term, "xterm") // Treat Terminal.app as generic xterm-like, for now. || nsterm; + bool hterm = terminfo_is_term_family(term, "hterm"); bool kitty = terminfo_is_term_family(term, "xterm-kitty"); bool linuxvt = terminfo_is_term_family(term, "linux"); bool bsdvt = terminfo_is_bsd_console(term); @@ -1705,7 +1706,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col unibi_set_bool(ut, unibi_back_color_erase, false); } - if (xterm) { + if (xterm || hterm) { // Termit, LXTerminal, GTKTerm2, GNOME Terminal, MATE Terminal, roxterm, // and EvilVTE falsely claim to be xterm and do not support important xterm // control sequences that we use. In an ideal world, these would have @@ -1714,9 +1715,13 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col // treatable as xterm. // 2017-04 terminfo.src lacks these. Xterm-likes have them. - unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;"); - unibi_set_if_empty(ut, unibi_from_status_line, "\x07"); - unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr"); + if (!hterm) { + // hterm doesn't have a status line. + unibi_set_if_empty(ut, unibi_to_status_line, "\x1b]0;"); + unibi_set_if_empty(ut, unibi_from_status_line, "\x07"); + // TODO(aktau): patch this in when DECSTBM is fixed (https://crbug.com/1298796) + unibi_set_if_empty(ut, unibi_set_tb_margin, "\x1b[%i%p1%d;%p2%dr"); + } unibi_set_if_empty(ut, unibi_enter_italics_mode, "\x1b[3m"); unibi_set_if_empty(ut, unibi_exit_italics_mode, "\x1b[23m"); @@ -1727,6 +1732,9 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col unibi_set_if_empty(ut, unibi_set_right_margin_parm, "\x1b[%i;%p2%ds"); } else { // Fix things advertised via TERM=xterm, for non-xterm. + // + // TODO(aktau): stop patching this out for hterm when it gains support + // (https://crbug.com/1175065). if (unibi_get_str(ut, unibi_set_lr_margin)) { ILOG("Disabling smglr with TERM=xterm for non-xterm."); unibi_set_str(ut, unibi_set_lr_margin, NULL); @@ -1875,6 +1883,8 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, const char *col && ((xterm && !vte_version) // anything claiming xterm compat // per MinTTY 0.4.3-1 release notes from 2009 || putty + // per https://chromium.googlesource.com/apps/libapps/+/a5fb83c190aa9d74f4a9bca233dac6be2664e9e9/hterm/doc/ControlSequences.md + || hterm // per https://bugzilla.gnome.org/show_bug.cgi?id=720821 || (vte_version >= 3900) || (konsolev >= 180770) // #9364 @@ -1959,6 +1969,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version, bool xterm = terminfo_is_term_family(term, "xterm") // Treat Terminal.app as generic xterm-like, for now. || nsterm; + bool hterm = terminfo_is_term_family(term, "hterm"); bool bsdvt = terminfo_is_bsd_console(term); bool dtterm = terminfo_is_term_family(term, "dtterm"); bool rxvt = terminfo_is_term_family(term, "rxvt"); @@ -1988,7 +1999,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version, "ext.resize_screen", "\x1b[8;%p1%d;%p2%dt"); } - if (putty || xterm || rxvt) { + if (putty || xterm || hterm || rxvt) { data->unibi_ext.reset_scroll_region = (int)unibi_add_ext_str(ut, "ext.reset_scroll_region", "\x1b[r"); @@ -2045,7 +2056,7 @@ static void augment_terminfo(TUIData *data, const char *term, long vte_version, // would use a tmux control sequence and an extra if(screen) test. data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, NULL, TMUX_WRAP(tmux, "\033]Pl%p1%06x\033\\")); - } else if ((xterm || rxvt || tmux || alacritty) + } else if ((xterm || hterm || rxvt || tmux || alacritty) && (vte_version == 0 || vte_version >= 3900)) { // Supported in urxvt, newer VTE. data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str(ut, "ext.set_cursor_color", |