diff --git a/autotests/folding/Makefile.fold b/autotests/folding/Makefile.fold index 1cea5dd..4223e0b 100644 --- a/autotests/folding/Makefile.fold +++ b/autotests/folding/Makefile.fold @@ -1,28 +1,28 @@ # comment include Makefile.in -include Makefile.doesntexist .PHONY: all all: target foo = bar $(var) \ $(baz) quux ifeq ($(CC),gcc) bla=$(call func,param1,param2) -else ifeq "x" "y"else ifeq "x" "y" +else ifeq "x" "y" $(error this seems wrong) -elseelse +else libs=$(normal_libs) endif target1: error target2: bla.cpp $@ $(CC) bla.c -o bla.o $(CC) bla.o $< \ -o bla.exe @echo "hello" diff --git a/autotests/folding/folding.cpp.fold b/autotests/folding/folding.cpp.fold index 14dccf2..9966389 100644 --- a/autotests/folding/folding.cpp.fold +++ b/autotests/folding/folding.cpp.fold @@ -1,15 +1,15 @@ /** * multi-line comment */ /* comment */ { { } { //BEGIN } //END } #if 0 -#elif 1#elif 1 -#else#else +#elif 1 +#else #endif diff --git a/autotests/folding/highlight.asp.fold b/autotests/folding/highlight.asp.fold index b938201..794097b 100644 --- a/autotests/folding/highlight.asp.fold +++ b/autotests/folding/highlight.asp.fold @@ -1,58 +1,58 @@ <% 'kate: hl ASP; if ( instr(request.servervariables("PATH_INFO"),"login.asp") <= 0 and instr(request.servervariables("PATH_INFO"),"inset") <= 0 and instr(request.servervariables("PATH_INFO"),"Data") <= 0 and instr(request.servervariables("PATH_INFO"),"dropDown") <= 0 ) then Session("originalRequestedPage") = Request.ServerVariables("PATH_INFO") & "?" & Request.ServerVariables("QUERY_STRING") end if function countRecords( rsToCount ) numRecs = 0 do until rsToCount.eof numRecs = numRecs + 1 rsToCount.movenext loop rsToCount.close ' just to make sure nobody ' tries to operate on the recordset, ' which has already reached eof countRecords = numRecs end function function unique( rs, sortColumn ) ' return unique instances of text in sortColumn within rs dim sorted() redim sorted(1) dim i i = 0 do until rs.eof if (not find( rs(sortColumn), sorted )) then redim preserve sorted(i+1) sorted(i) = rs(sortColumn) i = i + 1 end if rs.MoveNext loop redim preserve sorted(i-1) ' the function will add an extra blank entry to the array rs.Close ' close the recordset - we'll be using it again - and reset i - well be using it again, too unique = sorted end function sub testSub( variable ) ' do nothing impressive... dim newVar newVar = variable if ( variable = true ) response.end - else %>else %> + else %>

We are writing text.

<%=newVar%>

We have written text and outputted a variable.

<% end if end sub %> diff --git a/autotests/folding/highlight.cpp.fold b/autotests/folding/highlight.cpp.fold index 24c6949..65c8b43 100644 --- a/autotests/folding/highlight.cpp.fold +++ b/autotests/folding/highlight.cpp.fold @@ -1,507 +1,507 @@ #pragma once #include #include #include "assert.h" #include "assert.hpp" // abc #include "path/assert.hpp" #include "assert.h"a #include "assert.h" a #include a #include a #include FOO() error #include_next a #include_next /* a */ b #include PATH_IN_MACRO #include PATH_IN_MACRO() #include PATH_IN_MACRO(a, b) #define SOME_VAR 1 #ifdef SOME_VAR #define MULTILINE_MACRO one \ two \ three # define MULTILINE_MACRO_TEXT \ /* NOTE The contents of macro is too green :D */ \ char const s[] = "a\\b" \ "c\nd" # define VARIADIC(a, ...) \ f(a##a) \ f(__VA_ARGS__) \ f(#__VA_ARGS__) \ f(__VA_ARGS__) \ f(0 __VA_OPT__(,) __VA_ARGS__) \ x __VA_OPT__(= { __VA_ARGS__ }) # define MACRO() BAD \ ESCAPED # error dds # warning dds # line 2 "file.cpp" # define A(x, y) x##y x#y // OK(L, a) -> L"a" # define OK(x, y) x###y # define BAD(x, y) x####y # define A /* multi line with comment */ expr # define A /* multi line with comment */ 23 -#else // xelse // x +#else // x #42 // gcc extension = #line 42 // error #wrong # wrong #endif x #if DS() -#else xelse x -#else /* */xelse /* */x -#else /* xelse /* x +#else x +#else /* */x +#else /* x y */ z #endif // check that _XXX defines work, bug 397766 #ifndef _HEADER_GUARD #define _HEADER_GUARD 1 #endif #ifdef _HEADER_GUARD #if (_HEADER_GUARD >= 1) #endif #endif static int g_global; template class = std::is_pointer> struct class1 : private std::vector, public U { class1() try : _member1(xxx) {} catch(...) {} class1(class1&&) = default; ~class1() {} void foo() { return; } void foo() const { return; } void foo() noexcept { return; } void foo() const noexcept { return; } virtual void foo() const noexcept { return; } static void foo() { return; } constexpr static void foo() const noexcept(noexcept(std::is_pointer::value)) override { xxx::template ttt::type {}; xxx.template get(); xxx.std::rdbuf(); auto x = C<'a'> + y; } int operator->*(T (C::*m)(int)); operator value_t (); private: protected: public: value_type _member1; // NOTE internal ? value_type __internal; value_type internal__; value_type _M_internal; value_t member2_; value_type m_member3; static int s_static; static constexpr int s_static; static inline int s_static; static inline constexpr int s_static; }; constexpr struct : xyz { using xyz::xyz; using xyz::operator=; int a : 1; int b : 7; } x {}; template using is_pointer = std::is_pointer::type; template constexpr auto is_pointer_v = std::is_pointer::value; uint64_t namespaces() { std::vector; boost::vector; detail::vector; details::vector; aux::vector; internals::vector; other::vector; } #if 1 double foo(const A); -#else // else#else // else +#else // else double foo(const A); #endif // end #if 0 double foo(const A); -#else // else#else // else +#else // else double foo(const A); #endif // end #if 1 double foo(const A); -#elif 1#elif 1 +#elif 1 double foo(const A); #elif 0 double foo(const A); #endif // end #if 0 double foo(const A); -#elif 1#elif 1 +#elif 1 double foo(const A); -#elif 0#elif 0 +#elif 0 double foo(const A); #endif // end #if 0 double foo(const A); -#elif a#elif a +#elif a double foo(const A); #elif 0 double foo(const A); -#elif a#elif a +#elif a double foo(const A); -#else // elseelse // else +#else // else double foo(const A); #endif // end #if 0 // blah blah double foo(const A); -#elif 1 // blah blah#elif 1 // blah blah +#elif 1 // blah blah double foo(const A); -#else // else#else // else +#else // else double foo(const A); #endif // end #if 0 || a double foo(const A); -#else // elseelse // else +#else // else double foo(const A); #endif // end #if 1 || a double foo(const A); -#else // else#else // else +#else // else double foo(const A); #endif // end #if 0 && a double foo(const A); -#else // elseelse // else +#else // else double foo(const A); #endif // end #if 1 && a double foo(const A); -#else // elseelse // else +#else // else double foo(const A); #endif // end #if a double foo(const A); #elif 0 double foo(const A); #endif // end #if a double foo(const A); #elif 1 double foo(const A); #endif // end #if a double foo(const A); #elif a double foo(const A); #endif // end int bar(void*p, void * pp) { # if 0 double foo(); -# else // else# else // else +# else // else double foo(); # endif // end } #if abc 0 double foo(); #endif // end #if xxx double foo(); -#elseelse +#else double foo(); #endif // end #if xxx double foo(); #elif xxx // elseif double foo(); #elif xxx // elseif double foo(); #endif // end // error # #d # d #if #elif #endif #ifndef #endif #ifdef 0 #endif // end static uint64_t intWithSuffix = 42ull; static long intWithSuffixAndPrefix = 0b0101L; static int octNum = 07232; static int invalidOctNum = 09231; static uint64_t hexNum = 0xDEADBEEF42; static uint64_t invalidHexNum = 0xGLDFKG; static char binNum = 0b0101010; static int64_t intWithSuffix = -42LL; static long intWithSuffixAndPrefix = -0b0101L; static int octNum = -07232; static int invalidOctNum = -09231; static int64_t hexNum = -0xDEADBEEF42; static int64_t invalidHexNum = -0xGLDFKG; static char binNum = -0b0101010; static uint64_t intWithSuffixWithSep = 4'2ull; static long intWithSuffixAndPrefixWithSep = 0b0'10'1L; static int octNumWithSep = 07'232; static int invalidOctNumWithSep = 09'23'1; static uint64_t hexNumWithSep = 0xD'EAD'BE'EF'42; static uint64_t invalidHexNumWithSep = 0xGLD'FKG; static char binNumWithSep = 0b0'1010'10; static uint64_t invalidSep = 42'ull; static uint64_t invalidSep = 42'; static double d1 = 42.; static double d2 = .42; static double d2a = -0.49; static double d2b = -0.09; static double d3 = 42.3e1; static double d4 = .2e-12; static double d5 = 32.e+12; static double invalidD1 = 32.e+a12; static float floatQualifier = 23.123f; // Hexadecimal floating point (c++17) static double d6 = 0x1ffp10; static double d7 = 0X0p-1; static double d8 = 0x1.p0; static double d9 = 0xf.p-1L; static double d10 = 0x0.123p-1; static double d11 = 0xa.bp10l; static double invalidD2 = 0x0.123p-a; static float floatQualifier = 0xf.p-1f; 60min; // c++17 60.min; 60.3min; 0x1ffp10min; 2d; // c++20 23._f 23._fd 2.3_f 2.3_fd 2._f 2._fd 2e4_f 2e4_fd // error 23.fd 2e_fd 2e 1.y 1.0_E+2.0 1.0_E +2.0 // ok 1_p+2 1_p +2 // ok 4s.count() 4s. count() 4s .count() // ok static bool yes = true; static bool no = false; // *char* static const char c1 = 'c'; static const char c1a = u8'c'; // utf-8 char (c++17) static const char16_t c1b = u'c'; static const char32_t c1c = U'c'; static const wchar_t c1d = L'c'; static const char c2 = '\n'; static const char c2a = '\120'; // octal static const char c2b = '\x1f'; // hex static const char c2c = '\''; static const char c2d = '\\'; static const wchar_t c2e = L'\x1ff'; // hex static const wchar_t c2e = U'\x1fffffff'; // hex // error '\x123'; '\u1234'; '\U12345678'; U'\u12345'; u'\u123'; U'\U1234567'; U'\U123456789'; U'\x123456789'; // string static const char* c3 = "string"; static const char* c4 = "\"string\n\t\012\x12\""; static const char* c5 = "multiline \ string"; static const char* c6 = "multifragment" "other""string"; static const char* c6a = u8"string"; static const char16_t* c6b = u"string"; static const char32_t* c6c = U"string"; static const wchar_t* c6d = L"string"; static const char* rawString1 = R"(string)"; static const char* rawString1a = u8R"(string)"; static const char16_t* rawString1b = uR"(string)"; static const char32_t* rawString1c = UR"(string)"; static const wchar_t* rawString1d = LR"(string\nstring)"; static const char* rawString2 = R"ab(string\nstring)ab"; static const char* rawString3 = R"ab(string string)ab"; " %d %df fd" U"ds %d" R"(a%d)"; "\x123xsk"; u"\x123xsk"; // error u8"\x123xsk"; // UDL (c++11) operator""_a(const char*); operator ""_a(const char*); operator "" _a(const char*); // invalid suffix operator "" a(const char*); operator ""a(const char*); operator""a(const char*); "string"_s; // user "string"s; // standard "string"_s-b; // -b is not part of the string // Macro MY_XXX; BOOST_XXX; __STDC_VERSION__; __TIME__; __cplusplus; // Attributes [[noreturn]] void foo(); [[deprecated]] void foo(); [[deprecated("because")]] void foo(); void foo([[carries_dependency]] int); [[opt(1), debug]] [[using CC: opt(1), debug]] // c++17 [[using CC: CC::opt(1)]] // c++17 [[gnu::assume_aligned(3'2l,2)]] void* f(); [[using gnu: assume_aligned(3)]] [[clang::assume_aligned(3)]] void f([[maybe_unused]] int val) { [[maybe_unused]] int x; switch (x = foo(); x) { case 1: [[fallthrough]]; case XXX: case Class::foo(): [[fallthrough]]; default: ; } // c++17: fold expression (args + ... + (1 * 2)); (v.push_back(args), ...); [[omp::parallel]] for (auto&& x : v) x; for (auto&& [first,second] : mymap) { } auto [x, y] = foo(); [x = 1, =y](){}; decltype((auto)) x = foo(); } auto f() -> decltype(foo()); __attribute__((pure)) void f(); label: goto label; //BEGIN region // TODO comment FIXME comment ### comment BUG comment //END region // \brief blah blah /// \brief blah blah /** * Doxygen * @param p1 text * \brief bold text * \dot * a -> b * \enddot * * \verbatim * * \endverbatim * text */ #endif // Some GNU macros, cross-referenced from gcc.xml to isocpp.xml __GCC_ATOMIC_CHAR16_T_LOCK_FREE __GNUC__ __linux__ diff --git a/autotests/folding/highlight.f90.fold b/autotests/folding/highlight.f90.fold index 2ea0f76..94fe78d 100644 --- a/autotests/folding/highlight.f90.fold +++ b/autotests/folding/highlight.f90.fold @@ -1,181 +1,181 @@ ! This file is an example to test the syntax highlighting file F.xml ! (for fortran 90 and F) !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! THIS IS AN EXAMPLE OF A MODULE ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! module module_example ! use 'implicit none' when you want all variables to be declared implicit none !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! PUBLICS AND PRIVATES ! In fortran 90 you can define your own operator public :: operator(.norm.) public :: operator(+) ! <-- you can also overload the usual operators public :: factorial public :: example_fn private :: point3d_add private :: point3d_norm !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! USER-DEFINED TYPES... ! This is a definition to use in declarations of real variables, ! parameters, etc. integer, parameter, public :: kr = selected_real_kind(10) ! This is a user-defined type type, public :: point3d real(kind=kr) :: x, y, z end type point3d ! This type is useless: it is only an example of type definition! type, public :: example_type complex(kind=kr) :: c ! <-- a complex number (two reals of kind kr)! real, dimension(-10:10) :: & ! <-- this line does not end here! r1, r2 ! <-- this is the final part of the previous line real, pointer, dimension(:) :: pointer_to_array_of_real real, dimension(:), pointer :: array_of_pointer_to_real end type example_type !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! INTERFACES... ! Interface for the norm of a 3-D vector interface operator(.norm.) module procedure point3d_norm end interface ! Interface for the operator '+' interface operator(+) module procedure point3d_add end interface !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! SOME DECLARATIONS... ! A real number can be declared with the following line: real(kind=kr) :: real_var1 ! But if you are not interested on the precision of floating point numbers, ! you can use simply: real :: real_var2 ! An array can be declared in two ways: real(kind=kr), dimension(1:10, -4:5), private :: a, b, c real(kind=kr), private :: d(1:10, -4:5) ! This is a string with fixed lenght character(len=10) :: str_var ! This is an allocatable array, which can be a target of a pointer type(example_type), private, dimension(:), allocatable, target :: & many_examples ! Fortran 90 hasn't got its own preprocessor, it uses the C preprocessor! #ifdef XXX c <-- this is a comment in the old fortran 77 style (fixed form) c This is a free form file, so we shouldn't use this kind of comments! c But fortran 90 still understands fixed form, when parsing sources with c the *.f extension. c ! <-- this 'c' shouldn't be highlighted as a comment! #endif contains ! The sum of two points pure function point3d_add(a, b) result(rs) type(point3d) :: rs type(point3d), intent(in) :: a, b rs%x = a%x + b%x rs%y = a%y + b%y rs%z = a%z + b%z end function point3d_add ! The norm of a point pure function point3d_norm(a) result(rs) real(kind=kr) :: rs type(point3d), intent(in) :: a rs = sqrt(a%x * a%x + a%y * a%y + a%z * a%z) end function point3d_norm ! A simple recursive function recursive function factorial(i) result (rs) integer :: rs integer, intent(in) :: i if ( i <= 1 ) then rs = 1 - elseelse + else rs = i * factorial(i - 1) end if end function factorial ! This is a useless function subroutine example_fn(int_arg, real_arg, str_arg) integer, intent(in) :: int_arg real(kind=kr), intent(out) :: real_arg character(len=*), intent(in) :: str_arg type(example_type), pointer :: p integer :: n, i, j logical :: flag flag = .true. ! .true. is not an operator! if ( flag .and. flag ) then ! .and. is a pre-defined operator print *, "blabla" end if ! Examples of inquiry functions: allocated, lbound, ubound. if ( .not. allocated(many_examples) ) then allocate( many_examples(10) ) end if print *, "Lower bound = ", lbound(many_examples, 1) print *, "Upper bound = ", ubound(many_examples, 1) p => many_examples(5) ! <-- p is a pointer ! A strange way to calculate i*i: add the first i odd numbers i = 6 j = 0 do n = 1, i j = j + (2*n - 1) end do print *, "i*i = ", i*i, j real_arg = real(j) ! <-- here the highlighting is not very good: ! it is unable to distinguish between this and a definition like: ! real(kind=kr) :: a deallocate( many_examples ) end subroutine example_fn end module module_example !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! THIS IS THE MAIN PROGRAM ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! program example use module_example ! this is another example of use of the 'implicit' keyword implicit double precision (a-h,o-z) real(kind=kr) :: var_out type(point3d) :: & a = point3d(0.0_kr, 1.0_kr, 2.0_kr), & b = point3d(4.0_kr, 5.0_kr, 6.0_kr) print *, "a + b = ", .norm. (a + b) print *, "factorial of 5 = ", factorial(5) call example_fn(1, var_out, "hello!") end program example diff --git a/autotests/folding/highlight.lex.fold b/autotests/folding/highlight.lex.fold index b18a107..f6535bf 100644 --- a/autotests/folding/highlight.lex.fold +++ b/autotests/folding/highlight.lex.fold @@ -1,82 +1,82 @@ /* This test file tests kates Lex/Flex highlighting */ %option c++ %option yyclass="KateTester" %option yylineno /* This is a C(++) comment */ /* This one is a lex comment ! */ %{ #include #include "realparser.hpp" using namespace std; %} /* Some definitions */ DIGIT [0-9] LETTER [_a-zA-Z] -%%%% +%% /* Comment *shall be indented here* */ [ \t\n\r]+ /* Note: there is a bad } just here vvv */ \/\*([^\*]|\*[^/])*\*\/ { foo(a, b, c); } } /* A start condition scope... */ { "a" { /* C mode ! */ return 0; } "b" %{ /* C mode, too ! */ return 0; %} "c" return 0; // C++ comment } /* Big rule */ \"([^"\\]|\\.)*\" { yylval.string_val = new char[strlen(yytext) + 1]; int j = 0, i = 1; while (yytext[i] != '"') if (yytext[i] != '\\') yylval.string_val[j++] = yytext[i++]; else switch (yytext[i + 1]) { case 'n': yylval.string_val[j++] = '\n'; i += 2; break; default: yylval.string_val[j++] << yytext[i + 1], i += 2; } yylval.string_val[j] = 0; return TOK_STRING; } /* Dot (match all) */ . {return yylval.int_val = yytext[0];} -%%%% +%% // Here is pure C(++) #include int main(void) { std::cout << "Hello, World\n"; return 0; } diff --git a/autotests/folding/highlight.pl.fold b/autotests/folding/highlight.pl.fold index 6845697..f39931d 100644 --- a/autotests/folding/highlight.pl.fold +++ b/autotests/folding/highlight.pl.fold @@ -1,75 +1,75 @@ #!/usr/bin/perl -w # This is a pseudo Perl file to test Kate's Perl syntax highlighting. # TODO: this is incomplete, add more syntax examples! sub prg($) { my $var = shift; - $var =~ s/bla/foo/igs;/foo/igs; - $var =~ s!bla!foo!igs;!foo!igs; - $var =~ s#bla#foo#igs;#foo#igs; + $var =~ s/bla/foo/igs; + $var =~ s!bla!foo!igs; + $var =~ s#bla#foo#igs; $var =~ tr/a-z/A-Z/; ($match) = ($var =~ m/(.*?)/igs); $test = 2/453453.21; $test /= 2; print qq~d fsd fsdf sdfl sd~ $" = '/'; $foo = <<__EOF; d ahfdklf klsdfl sdf sd fsd sdf sdfsdlkf sd __EOF $x = "dasds"; next if( $match eq "two" ); next if( $match =~ /go/i ); @array = (1,2,3); # a comment @array = qw(apple foo bar); push(@array, 4); %hash = (red => 'rot', blue => 'blau'); print keys(%hash); } sub blah { my $str = << ' EOS'; this is my string and it's continuation EOS $str = "hello world"; $str = << " EOS"; this is my string and it's continuation EOS } &blah; prg("test"); # Bracket closures in RegExp patterns (bug #364866) qr{ ${var} aa{aa{a}a} aa*b?}; qr(aa(a(a(a(b|c)a)a)a)aa*b?); s{aaa {aaa} a \x{A2} *b?}{aa}; s(aa(a(a(a(b|c)a)a)a)aa)(aa); # Strings as scalar references (bug #348765) $x = \'Reference of a String'; $y = \"Reference of a String"; # Variables that start with underscore (bug #355300) $_variable $_ # Reserved var. for my $x ($hash->{arr}->@*) { for my $k (keys $k->%*) { ... } } diff --git a/autotests/folding/highlight.pony.fold b/autotests/folding/highlight.pony.fold index d89d314..9e708ef 100644 --- a/autotests/folding/highlight.pony.fold +++ b/autotests/folding/highlight.pony.fold @@ -1,219 +1,219 @@ // Test file for Pony syntax highlighting, released under MIT License primitive Red fun apply(): U32 => 0xFFFF0000 primitive Colours fun black(): U32 => 0xFF000000 fun red(): U32 => 0xFFFF0000 primitive Black primitive Blue type Colour is (Black | Blue ) primitive ColourList fun tag apply(): Array[Colour] => [Black; Blue] for colour in ColourList().values() do end type EGLEvent is (U8, F32, F32) (var code, var x, var y) = @getEvent[EGLEvent]() primitive _XDisplayHandle primitive _EGLDisplayHandle let x_dpy = @XOpenDisplay[Pointer[_XDisplayHandle]](U32(0)) if x_dpy.is_null() then env.out.print("XOpenDisplay failed") end let e_dpy = @eglGetDisplay[Pointer[_EGLDisplayHandle]](x_dpy) if e_dpy.is_null() then env.out.print("eglGetDisplay failed") end primitive _EGLConfigHandle let a = Array[U16](8) a.push(0x3040) a.push(0b01011) let config = Pointer[_EGLConfigHandle] if @eglChooseConfig[U32](e_dpy, a, config, U32(1), Pointer[U32]) == 0 then env.out.print("eglChooseConfig failed") end actor Main new create(env: Env) => // The no of arguments env.out.print(env.args.size().string()) for value in env.args.values() do env.out.print(value) end // Access the arguments the first one will always be the the appication name try env.out.print(env.args(0)?) end actor Main new create(env: Env) => var options = Options(env) options .add("output", "o", StringArgument) env.out.print(options.has_argument()) for option in options do match option | ("output", var arg: String) => _outputFileName = arg.string() | let err: ParseError => err.report(env.out) env.out.print( """ pony-embed [OPTIONS] --output name string output filename. """ ) end end use "ponytest" actor Main is TestList new create(env: Env) => PonyTest(env, this) new make() => None fun tag tests(test: PonyTest) => test(_TestAddition) class iso _TestAddition is UnitTest """ Adding 2 numbers """ fun name(): String => "u32/add" fun apply(h: TestHelper): TestResult => h.expect_eq[U32](2 + 2, 4) fun tag log(msg: String, verbose: Bool = false) be fail() => be assert_failed(msg: String) => fun tag assert_true(actual: Bool, msg: String = "") ? fun tag expect_true(actual: Bool, msg: String = ""): Bool fun tag expect_eq[A: (Equatable[A] #read & Stringable)] (expect: A, actual: A, msg: String = ""): Bool fun tag expect_eq[A: (Equatable[A] #unknown & Stringable)] (expect: A, actual: A, msg: String = ""): Bool fun add(other: A): A fun sub(other: A): A fun mul(other: A): A fun div(other: A): A fun mod(other: A): A fun eq(other: A): Bool fun ne(other: A): Bool fun lt(other: A): Bool fun le(other: A): Bool fun ge(other: A): Bool fun gt(other: A): Bool fun shl(other: A): A fun shr(other: A): A fun op_and(other:A): A fun op_or(other: A): A fun op_xor(othr: A): A class Test fun alpha() => """ """ let dice: Array[U32] = [1; 2; 3 4 5 6 ] actor Main fun foo(n:U32): {ref(U32): U32} => var s: Array[U32] = Array[U32].init(n, 1) {ref(i:U32)(s): U32 => try s(0) = s(0) + i s(0) - elseelse + else 0 end } new create(env:Env) => var f = foo(5) env.out.print(f(10).string()) env.out.print(f(20).string()) /* nested /* commentary */ */ // single comment class A class _A x' x'' x'.string() '\uaaaa' '\Ubbbbbb' '\xcc' '\'' '\n' "\uaaaaa" "\Ubbbbbbb" "\xccc" "\"" "\n" 34.4 34.4e43 43e4 0x3040 0xaF 0b01 3_43_4 0x0_4 fun create(): U32 => 0 fun iso create(): U32 => 0 fun \\ abc \\ iso create(): U32 => 0 class \\ packet, blah \\ iso Xyz if \\ likely \\ a then end a.endnormal print();print() /* syntactically false: */ class _aA class _a class a 0b2332 0b 0x 0xgf 0f00 3. .3 3.e3 3_ 3__43_4 '' '\u' '\ua' '\uaaa' '\uaaaaa' '\uyyyy' "\u" "\ua" "\uaaa" "\uyyyy" a'a class badType print(); diff --git a/autotests/folding/highlight.prg.fold b/autotests/folding/highlight.prg.fold index 9f03189..55ba4f4 100644 --- a/autotests/folding/highlight.prg.fold +++ b/autotests/folding/highlight.prg.fold @@ -1,71 +1,71 @@ // Test file to test kate's clipper highlighting // kate: hl Clipper; //BEGIN INCLUDES #include #include "logo.ch" #define PRGVERSION "0.0.1" //END //BEGIN CODE static ws, win static driver := getDriver() /* a multiline comment */ function main( formName ) local form local fileName if empty(formName) ?? "Usage: ./form_ui &\n" CANCEL - elseelse + else fileName := formName endif ws := UIWorkSpace() form := UIForm( fileName ) win := form:parseFile() // ?? valtype(win),chr(10) if win == NIL CANCEL endif win:show() ws:run() ws:quit() return 0 /* Setting dialog */ function settingsDialog() ?? "TODO: Settings dialog&\n" return /* About dialog */ function aboutDialog() local dlg := UIWindow("About", win, "aboutDlg", .F.) local hl, lside, t, bb, bD hl := UIHBox(,4,8) lside := UIVBox() lside:add(UIImage(eas_logo_mini,.T.)) hl:add(lside,.F.,.F.) dlg:userSpace:add(hl,.T.,.T.) t := UIVBox() hl:add(t,.T.,.T.) t:add(UILabel("License: GPL version 2 or later")) bb := UIButtonBar() t:add(bb) bD := UIButton(win, "&Close", {|o,e| dlg:close() } ) bb:add( bD ) dlg:setFocus(bD) dlg:setDefault(bD) dlg:setPlacement( .T. ) dlg:show() return //END diff --git a/autotests/folding/highlight.rb.fold b/autotests/folding/highlight.rb.fold index 9008000..713aefc 100644 --- a/autotests/folding/highlight.rb.fold +++ b/autotests/folding/highlight.rb.fold @@ -1,573 +1,573 @@ # This file is a testcase for the highlighting of Ruby code # It's not executable code, but a collection of code snippets # require 'Config' require 'DO/Clients' require 'DO/DBClients' def CGI::escapeElement(string, *elements) elements = elements[0] if elements[0].kind_of?(Array) unless elements.empty? string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/ni) do CGI::escapeHTML($&) end - elseelse + else string end end case inputLine when "debug" dumpDebugInfo dumpSymbols when /p\s+(\w+)/ dumpVariable($1) when "quit", "exit" exit - elseelse + else print "Illegal command: #{inputLine}" end kind = case year #hi there when 1850..1889 then "Blues" when 1890..1909 then "Ragtime" when 1910..1929 then "New Orleans Jazz" when 1930..1939 then "Swing" when 1940..1950 then "Bebop" - else "Jazz"else "Jazz" + else "Jazz" end # URL-encode a string. # url_encoded_string = CGI::escape("'Stop!' said Fred") # # => "%27Stop%21%27+said+Fred" def CGI::escape(string) string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do '%' + $1.unpack('H2' * $1.size).join('%').upcase end.tr(' ', '+') end # Class ClientManager # # definition : Import, store and export the various data used by the application. # This class is the sole interface between the application and the underlying database. mon, day, year = $1, $2, $3 if /(\d\d)-(\d\d)-(\d\d)/ puts "a = #{a}" if fDebug print total unless total == 0 while gets next if /^#/ # Skip comments parseLine unless /^$/ # Don't parse empty lines end if artist == "John Coltrane" #hi there artist = "'Trane" #hi there end unless nicknames == "no" #hi there handle = if aSong.artist == "Gillespie" then #hi there "Dizzy" - elsif aSong.artist == "Parker" thenelsif aSong.artist == "Parker" then + elsif aSong.artist == "Parker" then "Bird" - else #hi thereelse #hi there + else #hi there "unknown" end if aSong.artist == "Gillespie" then handle = "Dizzy" -elsif aSong.artist == "Parker" then handle = "Bird"elsif aSong.artist == "Parker" then handle = "Bird" -else handle = "unknown"else handle = "unknown" +elsif aSong.artist == "Parker" then handle = "Bird" +else handle = "unknown" end #hi there if aSong.artist == "Gillespie" then handle = "Dizzy" -elsif aSong.artist == "Parker" thenelsif aSong.artist == "Parker" then +elsif aSong.artist == "Parker" then handle = "Bird" -elseelse +else handle = "unknown" end if aSong.artist == "Gillespie" handle = "Dizzy" -elsif aSong.artist == "Parker"elsif aSong.artist == "Parker" +elsif aSong.artist == "Parker" handle = "Bird" -elseelse +else handle = "unknown" end case line when /title=(.*)/ puts "Title is #$1" when /track=(.*)/ puts "Track is #$1" when /artist=(.*)/ puts "Artist is #$1" end case shape when Square, Rectangle # ... when Circle # ... when Triangle # ... - elseelse + else # ... end until playList.duration > 60 #hi there playList.add(songList.pop) end 3.times do print "Ho! " end loop { # block ... } songList.each do |aSong| aSong.play end for aSong in songList aSong.play end for i in ['fee', 'fi', 'fo', 'fum'] print i, " " end for i in 1..3 print i, " " end for i in File.open("ordinal").find_all { |l| l =~ /d$/} print i.chomp, " " end class Periods def each yield "Classical" yield "Jazz" yield "Rock" end end periods = Periods.new for genre in periods print genre, " " end while gets next if /^\s*#/ # skip comments break if /^END/ # stop at end # substitute stuff in backticks and try again redo if gsub!(/`(.*?)`/) { eval($1) } # process line ... end i=0 loop do i += 1 next if i < 3 print i break if i > 4 end for i in 1..100 print "Now at #{i}. Restart? " retry if gets =~ /^y/i end def doUntil(cond) yield retry unless cond end i = 0 doUntil(i > 3) { print i, " " i += 1 } def system_call # ... code which throws SystemCallError -rescue SystemCallErrorrescue SystemCallError +rescue SystemCallError $stderr.print "IO failed: " + $! opFile.close File.delete(opName) raise end class ClientManager # constructor def initialize(dbase) @dbClient = DBClient.new(dbase) @clients = Clients.new end def prout(a, b, xy="jj") 24 end ############################################################### # # CLIENTS SECTION # ############################################################### # update the clients object by retrieving the related data from the database # returns the number of clients def refreshClients @clients.clean unless @sqlQuery.nil? then @sqlQuery.selectClient.each do |row| @clients.addClient(row[0],row[1],row[2],row[3],row[4],row[5], row[6], row[7], row[8]) end - elseelse + else puts "SqlQuery wasn't created : cannot read data from database" end return @clients.length end # insert a client in the database and refreshes the local clients object # we assume that none of the arguments is null # we assume that the client, identified by raison_sociale doesn't already exists def addClient(raison_sociale, division, departement, adresse, cp, ville, nom_contact, tel_contact) id = "0" unless @sqlQuery.nil? then id = @sqlQuery.insertClient(raison_sociale, division, departement, adresse, cp, ville, nom_contact,tel_contact) - elseelse + else puts "SqlQuery wasn't created : database update cannot be performed" end @clients.addClient(id, raison_sociale, division, departement, adresse, cp, ville, nom_contact, tel_contact) # synchronize local object with DB end # deletes a client from the database and updates the local Clients object accordingly def delClient(nomclient_brut) raison_sociale, div, dep = Clients.getIdentifiers(nomclient_brut) listeContratsExp, listeContratsSup, listeContratsProd, listePropositionsExp, listePropositionsSup = [] listeContratsExp = @contratsExpertise.getContratsFromClient(nomclient_brut) listeContratsSup = @contratsSupport.getContratsFromClient(nomclient_brut) listeContratsProd = @contratsProduits.getContratsFromClient(nomclient_brut) listePropositionsExp = @propositionsExpertise.getPropositionsFromClient(nomclient_brut) listePropositionsSup = @propositionsSupport.getPropositionsFromClient(nomclient_brut) unless @sqlQuery.nil? then @sqlQuery.deleteClient(raison_sociale, div, dep) @sqlQuery.deleteContracts(Config::EXPERTISE,listeContratsExp) @sqlQuery.deleteContracts(Config::SUPPORT,listeContratsSup) @sqlQuery.deleteContracts(Config::PRODUIT,listeContratsProd) @sqlQuery.deletePropositions(Config::EXPERTISE,listePropositionsExp) @sqlQuery.deletePropositions(Config::SUPPORT,listePropositionsSup) - elseelse + else puts "SqlQuery wasn't created : database update cannot be performed" end @clients.delClient(raison_sociale,div,dep) @contratsExpertise.deleteContracts(listeContratsExp) @contratsSupport.deleteContracts(listeContratsSup) @contratsProduits.deleteContracts(listeContratsProd) @propositionsExpertise.deletePropositions(listePropositionsExp) @propositionsSupport.deletePropositions(listePropositionsSup) end end # Mixin module for HTML version 3 generation methods. module Html3 # :nodoc: # The DOCTYPE declaration for this version of HTML def doctype %|| end # Initialise the HTML generation methods for this version. def element_init extend TagMaker methods = "" # - - for element in %w[ A TT I B U STRIKE BIG SMALL SUB SUP EM STRONG DFN CODE SAMP KBD VAR CITE FONT ADDRESS DIV center MAP APPLET PRE XMP LISTING DL OL UL DIR MENU SELECT table TITLE STYLE SCRIPT H1 H2 H3 H4 H5 H6 TEXTAREA FORM BLOCKQUOTE CAPTION ] methods += <<-BEGIN + nn_element_def(element) + <<-END def #{element.downcase}(attributes = {}) BEGIN end END end # - O EMPTY for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT ISINDEX META ] methods += <<-BEGIN + nOE_element_def(element) + <<-END def #{element.downcase}(attributes = {}) BEGIN end END end # O O or - O for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD LI OPTION tr th td ] methods += <<-BEGIN + nO_element_def(element) + <<-END def #{element.downcase}(attributes = {}) BEGIN end END end eval(methods) end end # following snippet from Webrick's log.rb # notice the erronous handling of the query method is_a? def format(arg) str = if arg.is_a?(Exception) "#{arg.class}: #{arg.message}\n\t" << arg.backtrace.join("\n\t") << "\n" - elsif arg.respond_to?(:to_str)elsif arg.respond_to?(:to_str) + elsif arg.respond_to?(:to_str) arg.to_str - elseelse + else arg.inspect end end # following snippet from Webrick's httputils.rb # Splitting regexps on several lines might be bad form, # but not illegal in Ruby. # This should probably be supported in the highlighting def split_header_value(str) str.scan(/((?:"(?:\\.|[^"])+?"|[^",]+)+) (?:,\s*|\Z)/xn).collect{|v| v[0] } end # snippet from Net::Telnet string.gsub(/#{IAC}( [#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]| [#{DO}#{DONT}#{WILL}#{WONT}] [#{OPT_BINARY}-#{OPT_NEW_ENVIRON}#{OPT_EXOPL}]| #{SB}[^#{IAC}]*#{IAC}#{SE} )/xno) # following snippet from Webrick's httpresponse.rb # the HEREDOC is not recognized as such @body << <<-_end_of_html_ #{HTMLUtils::escape(@reason_phrase)}

#{HTMLUtils::escape(@reason_phrase)}

#{HTMLUtils::escape(ex.message)}
_end_of_html_ # snippet from Webrick's httpproxy.rb # here we should make sure that the symbol definition ':' doesn't override # the module operator '::' Net::HTTP::version_1_2 if RUBY_VERSION < "1.7" # snippet from Webrick's cookie.rb # the beginning of the regexp is erronously highlighted like an operator key, val = x.split(/=/,2) # the following are division operators # it's a bit tricky to keep the operator apart from the regexp result = 8 / 4 result /= divisor # 2008-06-01 regexp and division operator syntax has been fixed: result = 8/4 # division result = 8/foo # division result = /8/ # regexp result = 8//4/ # division and regexp 10/10 # division 10/ 10 # division 10 /10 # division 10 / 10 # division foo/10 # division foo/ 10 # division foo /10/ # regexp foo / 10 # division foo/10/10 # both division total/count/2 # both division total/(count/2) # both division @foo/10 # division @foo /10 # division "hello"/10 # division "hello" / 10 # division /regexp//10 # division /regexp/ / 10 # division Math::PI/10 # division Math::foo /rx/ # regexp # 2008-06-05 similar fix for modulo operator: 10%4 # modulo 10 %4 # modulo 10% 4 # modulo 10 % 4 # modulo foo%4 # modulo # foo %4 # illegal %string foo% 4 # modulo foo % 4 # modulo foo % (4) # modulo foo %(4) # %string foo %q(4) # %string foo %Q(4) # %string foo %%4% # %string foo = %|blah| # GDL input foo % %|blah| # modulo and GDL # mix in any way you want result = 10//regexp//20/foo//regexp//20 # test cases for general delimited input # quoted strings %Q|this is a string| %Q{this is a string} %Q(this is a string) %Q> %Q[this is a string] %|also a string| %{also a string} %(also a string) %> %[also a string] # apostrophed strings %q|apostrophed| %q{apostrophed} %q(apostrophed) %q> %q[apostrophed] # regular expressions %r{expression} %r(expression) %r> %r[expression] %r|expression| # shell commands %x{ls -l} %x(ls -l) %x> %x[ls -l] # sometimes it's useful to have the command on multiple lines %x{ls -l | grep test } # alternative syntax `ls -l` `echo ' '` # token array %w{one two three} %w(one two three) %w> %w[one two three] # snippet from Net::IMAP # I object to putting String, Integer and Array into kernel methods. # While these classes are builtin in Ruby, this is an implementation detail # that should not be exposed to the user. # If we want to handle all std-lib classes, fine. But then they should be in their # own std-lib keyword category. def send_data(data) case data when nil put_string("NIL") when String send_string_data(data) when Integer send_number_data(data) when Array send_list_data(data) when Time send_time_data(data) when Symbol send_symbol_data(data) - elseelse + else data.send_data(self) end end # snippet from Net::POP # class names can have numbers in them as long as they don't begin with numbers # Ruby doesn't internally really make much of a difference between a class name and a constant # class aliases POP = POP3 POPSession = POP3 POP3Session = POP3 # "member access" POP::Session.COUNT.attribute.calc_with(2){ |arg| puts arg } # snippet from Net::SMTP # This breaks the code folding. I think we would need to # handle the '\' that continues the statement to the next line # in some way to make these kind of things not break something. raise ArgumentError, 'both user and secret are required'\ unless user and secret # string escapes must detect escaping the escape char str = "\\" str = "\\\\" # this is not part of the string %x{echo \\\}\\} # prints \}\ # this is not part of the command # these are all symbols :abc :abc! :abc? :abc= :[] :[]= :@abc :@@abc :$abc # squiggly HEREDOC <<~HEREDOC Hello!! HEREDOC # HEREDOC with backticks <<`HEREDOC` echo "hello" HEREDOC # do not highlight HEREDOC markers after the "class" keyword # (singleton class definition) (bug: #358273) class <end singleton_class = ( class <end ) # highlight regular expressions after ": " (bug: #361875) get 'files/:slug/:filename', to: 'files#download', slug: /^[a-z]+$/, filename: %r|^[/\s]+$| @@hello!: /regexp/ []=: %r!regexp! diff --git a/autotests/folding/highlight.spec.fold b/autotests/folding/highlight.spec.fold index 6ed00cf..4ecb442 100644 --- a/autotests/folding/highlight.spec.fold +++ b/autotests/folding/highlight.spec.fold @@ -1,212 +1,212 @@ # Test file for rpmspec.xml # Comments start with a # in column="0": # Some comment # When they don't start in column="0", that they are recognized as comments, but with an alert: # This is a bad comment. # RPM spec says clear that comments must start at the begin of the line. However, in practice # the RPM software is more permissive, depending on the context. But for our syntax highlighting, # we give, while recognizing the as comment, at least a little alert. Comments should not contain # the character % (which is marked as warning), but 2 of them are okay: %%. TODO is higlighted. # A spec file starts with "Normal" context. Here, you can specify values for some tags: Name: kradioripper-unstable # Note that here in no comment possible! Name: name only _one_ word allowed Name: %macro no further syntax check after macro! # Some tags support only _one_ word as value Version: 0.4test5 up-from-the-space-this-is-an-error # Some tag can have parameters: Any char in paranthesis: Summary: Recorder for internet radios (based on Streamripper) Summary(de.UTF-8): Aufnahmeprogramm für Internetradios (basiert auf Streamripper) # requiere free text: License: License 1 2 3 # requiere a well defines value: Requires( / ( = ): Some, value() # new type "switch" accepts: yes, no, 0, 1 AutoReq: yes AutoReq: yes invalid AutoReq: %macro no further syntax check after macro! AutoReq: no AutoReq: 0 AutoReq: 1 # requiere a number: Epoch: 123123 Epoch: 123123 invalid Epoch: %macro no further syntax check afer macro! # If tags are used that are not known, they are not highlighted: Invalidtag: Some value Invalid content in this section (only tags are allowed) # You can use conditions in specs (highlighted with region markers): %if 0%{?mandriva_version} # numbers and strings are distingished: string: %if lsdksfj # number: %if 23472398 # string: %if lksdjfsl72939 # invalid: %if 92437lsdkfjdsl # valid: %if "lsdfj %ksdf(sdfs) 3489" Release: %mkrel 1.2 -%else %else +%else Release: 0 %endif # requiere a well defined value: %ifos fixed_value # You must use these special macros (%%if etc.) always at the start of the line - if not, # that's bad but not an arror. You must also always use the specified form. Everything else is an # error: %if something %if %{if} %if(some options) # However, this are different macros and therefore correct: %ifx %{ifx} %ifx(some options) # the \ is escaped in the line. At the end of the line it escapes the line break: echo This is \" a text \\ and here\ it continues. %define name value %define invalid_näme value %define macroname multi\ line content with references like %0 %* %# %{-f} %{-f*} %1 %2 and so on %global name value %global invalid_näme value %undefine name %undefine name too-many-parameters # This special comment is treated and highlighted like a tag: # norootforbuild # It can't have parameters, so every following non-whitespace character is not good: # norootforbuild DONT WRITE ANYTHING HERE! # wrong spacing is also recognized: # norootforbuild # and also an indeet is not fine for norootforbuild: # norootforbuild # This following "Conflicts" tag will be removed by set-version.sh, # if it is a "kradioripper" release (and not a "kradioripper-unstable" release)... Conflicts: kradioripper %description # Here, a new section starts. It contains a value for the RPM field "description" and is therefor # colored like values: A KDE program for ripping internet radios. Based on StreamRipper. # A section start can have parameters: %description -l de.UTF-8 Ein KDE-Aufnahmeprogramm für Internetradios. Basiert auf StreamRipper. # These sections starts are errors: %description not at the first line %{description} wrong form %description(no options allowed, only parameters!) %prep # This starts a section that defines the commands to prepare the build. # q means quit. n sets the directory: %setup -q -n kradioripper echo Test # Macros can have different forms: Valid: %abc %abcÄndOfMacro %abc(def)EndOfMacro %{abc}EndOfMacro %{something but no single %}EndOfMacro %{abc:def}EndOfMacro %(abc) # Invalid: %ÄInvalidChar % %) %} # You can use macros inside of macro calls: Fine: %{something %but no %{sin%(fine)gle} }EndOfMacro # Bad: %{No closing paranthesis (No syntax highlightig for this error available) %build cmake ./ -DCMAKE_INSTALL_PREFIX=%{_prefix} %__make %{?jobs:-j %jobs} %install %if 0%{?suse_version} %makeinstall %suse_update_desktop_file kradioripper %endif %if 0%{?fedora_version} || 0%{?rhel_version} || 0%{?centos_version} make install DESTDIR=%{buildroot} desktop-file-install --delete-original --vendor fedora --dir=%{buildroot}/%{_datadir}/applications/kde4 %{buildroot}/%{_datadir}/applications/kde4/kradioripper.desktop %endif %if 0%{?mandriva_version} %makeinstall_std %endif %clean rm -rf "%{buildroot}" %files %defattr(-,root,root) %if 0%{?fedora_version} || 0%{?rhel_version} || 0%{?centos_version} %{_datadir}/applications/kde4/fedora-kradioripper.desktop -%else %else +%else %{_datadir}/applications/kde4/kradioripper.desktop %endif %{_bindir}/kradioripper %{_datadir}/locale/*/LC_MESSAGES/kradioripper.mo %if 0%{?mandriva_version} # TODO The %%doc macro is actually broken for mandriva 2009 in build service... %dir %{_datadir}/apps/kradioripper %{_datadir}/apps/kradioripper/* -%else %else +%else %doc COPYING LICENSE LICENSE.GPL2 LICENSE.GPL3 NEWS WARRANTY %dir %{_datadir}/kde4/apps/kradioripper %{_datadir}/kde4/apps/kradioripper/* %endif %changelog * Sun May 04 2008 email@email.com - some text - some text in two lines - some text in two lines + with subtext - and more subtext in two lines * Tue Apr 24 2007 Name - text * When the star isn't at column 0, than it doesn't indicate a new date * Wen Sep 08 2003 Wrong weekday * Mon Mai 08 2003 Wrong month * Mon Sep 0 2003 bad day * Mon Sep 8 2003 good day * Mon Sep 08 2003 good day * Mon Sep 32 2003 bad day * Mon Sep 08 03 bad year * Mon Sep 08 2003 Name # When using macros, the error check is disabled: * %myDataMacro Title of the entry - Text - can - be - indeeded - without - problems diff --git a/autotests/folding/highlight.t2t.fold b/autotests/folding/highlight.t2t.fold index 428f204..e733d68 100644 --- a/autotests/folding/highlight.t2t.fold +++ b/autotests/folding/highlight.t2t.fold @@ -1,90 +1,90 @@ txt2tags sample %!--includeconf: config.t2t % disabled here because there is no external file %!preproc: 'JUST A TEST' ' ' %!postproc: '(?i)()' ' shots\1' %!--include: menu.t2t -= Title 1 == Title 1 = += Title 1 = -== My Subtitle 1 ==[some definition]== My Subtitle 1 ==[some definition] +== My Subtitle 1 ==[some definition] Some examples: - A paragraph with **bold**, //italic// and --strike--. - You can even __underline your docs__! - And use **//bold and italic//** - or //**italic and bold**// Here is a nice pic: [img/t2tpowered.png]. - And a [link to a cool website http://txt2tags.sf.net]! - A table : || Name | Age | Gender | | John | 33 | Male | | Mary | 19 | Female | ``` A verbatim line And it's working for ``special code`` like this. ``` Unfortunately I can't color this verbatim content yet. ``` -== My Subtitle 2 ==== My Subtitle 2 == +== My Subtitle 2 == Lorem ipsum etc Lorem ipsum etc Lorem ipsum etc - Test d'écriture avec des accents à la française. Ça marche ou pas ? -== My Subtitle 3 ==== My Subtitle 3 == +== My Subtitle 3 == Lorem ipsum etc Lorem ipsum etc Here is a direct link: http://kde.org - Another boring part... -=== My Subsubtitle 1 ====== My Subsubtitle 1 === +=== My Subsubtitle 1 === //It's a level 3 header// - list 1 - list 2 - list 2b - list 3 -=== My Subsubtitle 2 ====== My Subsubtitle 2 === +=== My Subsubtitle 2 === //It's another level 3 header// + ordered list 1 + list 2 + list 2B + list 2C + list 3 -== My Subtitle 4 ==== My Subtitle 4 == +== My Subtitle 4 == nothing to say here... diff --git a/autotests/folding/highlight.y.fold b/autotests/folding/highlight.y.fold index d977948..dfae62c 100644 --- a/autotests/folding/highlight.y.fold +++ b/autotests/folding/highlight.y.fold @@ -1,95 +1,95 @@ /* Yacc / Bison hl test file. * It won't compile :-) Sure ! */ %{ #include using namespace std; extern KateParser *parser; %} %locations %union { int int_val; double double_val; bool bool_val; char *string_val; char *ident_val; struct var *v; void *ptr; } %token TOK_NOT_EQUAL "!=" %token TOK_LESSER_E "<=" %token TOK_GREATER_E ">=" %token TOK_EQUAL_2 "==" //comment %token PERCENT_DEBUG "%debug" PERCENT_DEFAULT_PREC "%default-prec" PERCENT_DEFINE "%define" ; %type type type_proc %code top { #define _GNU_SOURCE #include int val; } %destructor { free ($$); printf ("%d", @$.first_line); } <*> %lex-param {scanner_mode *mode}; %parse-param {int *nastiness} {int *randomness} %initial-action { @$.initialize (file_name); }; -%%%% +%% prog: KW_PROGRAM ident { parser->start($2); } prog_beg_glob_decl instructions { parser->endproc(0); } dev_procedures KW_ENDP ; number: integer_number | TOK_DOUBLE { $$ = new var; $$->type = KW_REEL; $$->cl = var::LITTERAL; $$->real = $1; }; words: %empty | words word ; %type word; %printer { fprintf (yyo, "%s", word_string ($$)); } ; word: %?{ boom(1); } | "hello" { $$ = hello; } | "bye" { $$ = bye; } ; foo: { $$ = 0 } | number { $$ = $1 | $2; } | hello { $$ = $1 | $3; } // without a comma hello: gram1 { $$ = "hi" }; | gram2 ;; -%%%% +%% #include int main(void) { puts("Hello, World!"); return 0; } // ALERT NOTE diff --git a/autotests/folding/test.desktop.fold b/autotests/folding/test.desktop.fold index 77db817..7a204f4 100644 --- a/autotests/folding/test.desktop.fold +++ b/autotests/folding/test.desktop.fold @@ -1,19 +1,33 @@ # test file for .dekstop syntax highlighting -[Desktop Entry][Desktop Entry] +[Desktop Entry] GenericName=Text Editor GenericName[ar]=محرّر نصوص Name=KWrite Name[ar]=كاتبك Comment=KDE Text Editor Comment[ar]=محرّر نصوص لكدي MimeType=text/plain; Exec=kwrite %U StartupNotify=true Icon=kwrite X-DocPath=kwrite/index.html Type=Application Terminal=false InitialPreference=8 X-DBUS-StartupType=Multi X-DBUS-ServiceName=org.kde.kwrite Categories=Qt;KDE;Utility;TextEditor; +Actions=new-window;new-tab; + +# test folding: the header should not be +# part of the previous region. +# NOTE: this options in Exec don't exist. +[Desktop Action new-window] +Name=New Window +Exec=kwrite --new-window +Icon=kwrite + +[Desktop Action new-tab] +Name=New Tab +Exec=kwrite --new-tab +Icon=kwrite diff --git a/autotests/folding/test.ini.fold b/autotests/folding/test.ini.fold index 27d80c8..2353cb1 100644 --- a/autotests/folding/test.ini.fold +++ b/autotests/folding/test.ini.fold @@ -1,18 +1,18 @@ ; comment ; comment with ### alerts # alternative comments -[Empty section][Empty section] +[Empty section] -[Section 2][Section 2] +[Section 2] Key1=String Value Key2=42 Key3=3.14 Key\SubKey=True -[Section 3][Section 3] +[Section 3] ; = in values are fine ; inline comments are not supported, ;/# are part of the value key4 = foo = True; bar = False key4b = abcd#1234! key5/subkey = "foo\nbar" diff --git a/autotests/folding/usr.bin.apparmor-profile-test.fold b/autotests/folding/usr.bin.apparmor-profile-test.fold index 64deede..d44d9b0 100644 --- a/autotests/folding/usr.bin.apparmor-profile-test.fold +++ b/autotests/folding/usr.bin.apparmor-profile-test.fold @@ -1,270 +1,270 @@ # Sample AppArmor Profile. # License: Public Domain # NOTE: This profile is not fully functional, since # it is designed to test the syntax highlighting. include # Variable assignment @{FOO_LIB}=/usr/lib{,32,64}/foo @{USER_DIR} = @{HOME}/Public @{HOME}/Desktop #No-Comment @{USER_DIR} += @{HOME}/Hello \ deny owner #No-comment aa#aa ${BOOL} = true # Alias alias /usr/ -> /mnt/usr/, # Profile for /usr/bin/foo profile foo /usr/bin/foo flags=(attach_disconnected enforce) { #include #include #include"/etc/apparmor.d/abstractions/ubuntu-konsole" include "/etc/apparmor.d/abstractions/openssl" include if exists include #include /some/file mr, #include /bin/true Px, # File rules /{,**/} r, owner /{home,media,mnt,srv,net}/** r, owner @{USER_DIR}/** rw, audit deny owner /**/* mx, /**.[tT][xX][tT] r, # txt owner file @{HOME}/.local/share/foo/{,**} rwkl, owner @{HOME}/.config/*.[a-zA-Z0-9]* rwk, "/usr/share/**" r, "/var/lib/flatpak/exports/share/**" r, "/var/lib/{spaces in string,hello}/a[^ a]a/**" r, allow file /etc/nsswitch.conf r, allow /etc/fstab r, deny /etc/xdg/{autostart,systemd}/** r, deny /boot/** rwlkmx, owner @{PROC}/@{pid}/{cmdline,mountinfo,mounts,stat,status,vmstat} r, /sys/devices/**/uevent r, @{FOO_LIB}/{@{multiarch},64}/** mr, /usr/bin/foo ixr, /usr/bin/dolphin pUx, /usr/bin/* Pixr, /usr/bin/khelpcenter Cx -> sanitized_helper, /usr/bin/helloworld cxr -> hello_world, # Dbus rules dbus (send) #No-Comment bus=system path=/org/freedesktop/NetworkManager interface=org.freedesktop.DBus.Introspectable peer=(name=org.freedesktop.NetworkManager label=unconfined), dbus (send receive) bus=system path=/org/freedesktop/NetworkManager interface=org.freedesktop.NetworkManager member={Introspect,state} peer=(name=(org.freedesktop.NetworkManager|org.freedesktop.DBus)), dbus (send) bus=session path=/org/gnome/GConf/Database/* member={AddMatch,AddNotify,AllEntries,LookupExtended,RemoveNotify}, dbus (bind) bus=system name=org.bluez, # Signal rules signal (send) set=(term) peer="/usr/lib/hello/world// foo helper", signal (send, receive) set=(int exists rtmin+8) peer=/usr/lib/hello/world//foo-helper, # Child profile profile hello_world { # File rules (three different ways) file /usr/lib{,32,64}/helloworld/**.so mr, /usr/lib{,32,64}/helloworld/** r, rk /usr/lib{,32,64}/helloworld/hello,file, # Link rules (two ways) l /foo1 -> /bar, link /foo2 -> bar, link /foo3 to bar, link subset /link* -> /**, # Network rules network inet6 tcp, network netlink dgram, network bluetooth, network unspec dgram, # Capability rules capability dac_override, capability sys_admin, capability sys_chroot, # Mount rules mount options=(rw bind remount nodev noexec) vfstype=ecryptfs /home/*/.helloworld/ -> /home/*/helloworld/, mount options in (rw, bind) / -> /run/hellowordd/*.mnt, mount option=read-only fstype=btrfs /dev/sd[a-z][1-9]* -> /media/*/*, umount /home/*/helloworld/, # Pivot Root rules pivot_root oldroot=/mnt/root/old/ /mnt/root/, pivot_root /mnt/root/, # Ptrace rules ptrace (trace) peer=unconfined, ptrace (read, trace, tracedby) peer=/usr/lib/hello/helloword, # Unix rules unix (connect receive send) type=(stream) peer=(addr=@/tmp/ibus/dbus-*,label=unconfined), unix (send,receive) type=(stream) protocol=0 peer=(addr=none), unix peer=(label=@{profile_name},addr=@helloworld), # Rlimit rule set rlimit data <= 100M, set rlimit nproc <= 10, set rlimit memlock <= 2GB, set rlimit rss <= infinity, # Change Profile rules change_profile unsafe /** -> [^u/]**, change_profile unsafe /** -> {u,un,unc,unco,uncon,unconf,unconfi,unconfin,unconfine}, change_profile /bin/bash -> new_profile//hat, } # Hat ^foo-helper\/ { network unix stream, unix stream, /usr/hi\"esc\x23esc\032es\477esc\*es\{esc\ rw r, # Escape expressions # Text after a variable is highlighted as path file /my/path r, @{FOO_LIB}file r, @{FOO_LIB}#my/path r, #Comment @{FOO_LIB}ñ* r, unix (/path\t{aa}*,*a @{var}*path,* @{var},*), } } # Syntax Error /usr/bin/error (complain, audit) { file #include /hello r, # Error: Variable open or with characters not allowed @{var @{sdf&s} # Error: Open brackets /{hello{ab,cd}world kr, /{abc{abc kr, /[abc kr, /(abc kr, # Error: Empty brackets /hello[]hello{}hello()he kr, # Comments not allowed dbus (send) #No comment path=/org/hello #No comment interface=org.hello #No comment peer=(name=org.hello #No comment label=unconfined), #Comment @{VARIABLE} = val1 val2 val3 #No comment # Error: Open rule /home/*/file rw - capability dac_overridecapability dac_override + capability dac_override deny file /etc/fstab w audit network ieee802154, dbus (receive - unix stream,unix stream, + unix stream, unix stream, } profile other_tests { # set rlimit set rlimit nice <= 3, rlimit nice <= 3, # Without "set" set #comment rlimit nice <= 3, # "remount" keyword mount remount remount, remount remount remount, dbus remount - remount,remount, + remount, unix remount - remount,remount, + remount, # "unix" keyword network unix unix, ptrace unix - unix,unix, + unix, unix unix - unix,unix, + unix, # Transition rules /usr/bin/foo cx -> hello*, /usr/bin/foo Cx -> path/, /usr/bin/foo cx -> ab[ad/]hello, /usr/bin/foo Cx -> ab[cd/]a[ad/]hello/path, /usr/bin/foo Cx -> ab[hello/path, /usr/bin/foo cx -> "hello*", /usr/bin/foo Cx -> "path/", /usr/bin/foo cx -> "ab[ad/]hello", /usr/bin/foo Cx -> "ab[cd/]a[ad/]hello/path", /usr/bin/foo Cx -> "ab[hello/path", /usr/bin/foo cx -> holas//hello/sa, /usr/bin/foo cx -> df///dd//hat, /usr/bin/foo cx -> holas,#sd\323fsdf, # Access modes /hello/lib/foo rwklms, # s invalid /hello/lib/foo rwmaix, # w & a incompatible /hello/lib/foo kalmw, /hello/lib/foo wa, # OK /hello/lib/foo rrwrwwrwrw, /hello/lib/foo ixixix, # Incompatible exec permissions ixixux, uxuxUxux, ixixixPixix, ixixpx uxuxuxPuxux, UxUxcUxUx, pixpixcixix, cxcxcxix, pixpixpux pixpixix xxix xxpux ixixx puxpuxx, Cuxcux Pixpix, puxpUx puxPUx xxpix xxcx, # Test valid permissions r w a k l m l x ix ux Ux px Px cx Cx , pix Pix cix Cix pux Pux cux Cux pUx PUx cUx CUx, rwklmx raklmx, r rw rwk rwkl rwklm, rwlmix rwlmUx rwlmPx rwlmcx rwlmPUx, rwixixixkl rwUxUxUxkl rwuxuxuxk rwpxpxpxk rwPxPxkl rwcxcxlm rwCxCxk, rwpixpixk rwPixPixkl wrpuxpuxk rwpUxpUxk rwcixcixcixml rwCixCixk rwCuxCuxk rwCUxCUxl, # Profile name profile holas { ... } profile { ... } profile /path { ... } profile holas/abc { ... } profile holas\/abc { ... } profile #holas { ... } profile flags=(complain)#asd { ... } profile flags flags=(complain) { ... } profile flags(complain) { ... } } diff --git a/autotests/html/test.desktop.html b/autotests/html/test.desktop.html index 66cdb27..0a2acc2 100644 --- a/autotests/html/test.desktop.html +++ b/autotests/html/test.desktop.html @@ -1,26 +1,40 @@ test.desktop
 # test file for .dekstop syntax highlighting
 [Desktop Entry]
 GenericName=Text Editor
 GenericName[ar]=محرّر نصوص
 Name=KWrite
 Name[ar]=كاتبك
 Comment=KDE Text Editor
 Comment[ar]=محرّر نصوص لكدي
 MimeType=text/plain;
 Exec=kwrite %U
 StartupNotify=true
 Icon=kwrite
 X-DocPath=kwrite/index.html
 Type=Application
 Terminal=false
 InitialPreference=8
 X-DBUS-StartupType=Multi
 X-DBUS-ServiceName=org.kde.kwrite
 Categories=Qt;KDE;Utility;TextEditor;
+Actions=new-window;new-tab;
+
+# test folding: the header should not be
+# part of the previous region.
+# NOTE: this options in Exec don't exist.
+[Desktop Action new-window]
+Name=New Window
+Exec=kwrite --new-window
+Icon=kwrite
+
+[Desktop Action new-tab]
+Name=New Tab
+Exec=kwrite --new-tab
+Icon=kwrite
 
diff --git a/autotests/input/test.desktop b/autotests/input/test.desktop index f3edd78..9212aaf 100644 --- a/autotests/input/test.desktop +++ b/autotests/input/test.desktop @@ -1,19 +1,33 @@ # test file for .dekstop syntax highlighting [Desktop Entry] GenericName=Text Editor GenericName[ar]=محرّر نصوص Name=KWrite Name[ar]=كاتبك Comment=KDE Text Editor Comment[ar]=محرّر نصوص لكدي MimeType=text/plain; Exec=kwrite %U StartupNotify=true Icon=kwrite X-DocPath=kwrite/index.html Type=Application Terminal=false InitialPreference=8 X-DBUS-StartupType=Multi X-DBUS-ServiceName=org.kde.kwrite Categories=Qt;KDE;Utility;TextEditor; +Actions=new-window;new-tab; + +# test folding: the header should not be +# part of the previous region. +# NOTE: this options in Exec don't exist. +[Desktop Action new-window] +Name=New Window +Exec=kwrite --new-window +Icon=kwrite + +[Desktop Action new-tab] +Name=New Tab +Exec=kwrite --new-tab +Icon=kwrite diff --git a/autotests/reference/test.desktop.ref b/autotests/reference/test.desktop.ref index 12ca73f..47b5c58 100644 --- a/autotests/reference/test.desktop.ref +++ b/autotests/reference/test.desktop.ref @@ -1,19 +1,33 @@ # test file for .dekstop syntax highlighting
[Desktop Entry]

GenericName=Text Editor
GenericName[ar]=محرّر نصوص
Name=KWrite
Name[ar]=كاتبك
Comment=KDE Text Editor
Comment[ar]=محرّر نصوص لكدي
MimeType=text/plain;
Exec=kwrite %U
StartupNotify=true
Icon=kwrite
X-DocPath=kwrite/index.html
Type=Application
Terminal=false
InitialPreference=8
X-DBUS-StartupType=Multi
X-DBUS-ServiceName=org.kde.kwrite
Categories=Qt;KDE;Utility;TextEditor;
+Actions=new-window;new-tab;
+
+# test folding: the header should not be
+# part of the previous region.
+# NOTE: this options in Exec don't exist.
+
[Desktop Action new-window]

+Name=New Window
+Exec=kwrite --new-window
+Icon=kwrite
+
+
[Desktop Action new-tab]

+Name=New Tab
+Exec=kwrite --new-tab
+Icon=kwrite
diff --git a/src/lib/abstracthighlighter.cpp b/src/lib/abstracthighlighter.cpp index 159fb52..fd6a9e9 100644 --- a/src/lib/abstracthighlighter.cpp +++ b/src/lib/abstracthighlighter.cpp @@ -1,359 +1,366 @@ /* Copyright (C) 2016 Volker Krause Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "abstracthighlighter.h" #include "abstracthighlighter_p.h" #include "context_p.h" #include "definition_p.h" #include "foldingregion.h" #include "format.h" #include "repository.h" #include "rule_p.h" #include "state.h" #include "state_p.h" #include "ksyntaxhighlighting_logging.h" #include "theme.h" using namespace KSyntaxHighlighting; AbstractHighlighterPrivate::AbstractHighlighterPrivate() { } AbstractHighlighterPrivate::~AbstractHighlighterPrivate() { } void AbstractHighlighterPrivate::ensureDefinitionLoaded() { auto defData = DefinitionData::get(m_definition); if (Q_UNLIKELY(!m_definition.isValid() && defData->repo && !m_definition.name().isEmpty())) { qCDebug(Log) << "Definition became invalid, trying re-lookup."; m_definition = defData->repo->definitionForName(m_definition.name()); defData = DefinitionData::get(m_definition); } if (Q_UNLIKELY(!defData->repo && !defData->fileName.isEmpty())) qCCritical(Log) << "Repository got deleted while a highlighter is still active!"; if (m_definition.isValid()) defData->load(); } AbstractHighlighter::AbstractHighlighter() : d_ptr(new AbstractHighlighterPrivate) { } AbstractHighlighter::AbstractHighlighter(AbstractHighlighterPrivate *dd) : d_ptr(dd) { } AbstractHighlighter::~AbstractHighlighter() { delete d_ptr; } Definition AbstractHighlighter::definition() const { return d_ptr->m_definition; } void AbstractHighlighter::setDefinition(const Definition &def) { Q_D(AbstractHighlighter); d->m_definition = def; } Theme AbstractHighlighter::theme() const { Q_D(const AbstractHighlighter); return d->m_theme; } void AbstractHighlighter::setTheme(const Theme &theme) { Q_D(AbstractHighlighter); d->m_theme = theme; } /** * Returns the index of the first non-space character. If the line is empty, * or only contains white spaces, text.size() is returned. */ static inline int firstNonSpaceChar(const QString & text) { for (int i = 0; i < text.length(); ++i) { if (!text[i].isSpace()) { return i; } } return text.size(); } State AbstractHighlighter::highlightLine(const QString& text, const State &state) { Q_D(AbstractHighlighter); // verify definition, deal with no highlighting being enabled d->ensureDefinitionLoaded(); const auto defData = DefinitionData::get(d->m_definition); if (!d->m_definition.isValid() || !defData->isLoaded()) { applyFormat(0, text.size(), Format()); return State(); } // verify/initialize state auto newState = state; auto stateData = StateData::get(newState); const DefinitionRef currentDefRef(d->m_definition); if (!stateData->isEmpty() && (stateData->m_defRef != currentDefRef)) { qCDebug(Log) << "Got invalid state, resetting."; stateData->clear(); } if (stateData->isEmpty()) { stateData->push(defData->initialContext(), QStringList()); stateData->m_defRef = currentDefRef; } // process empty lines if (text.isEmpty()) { /** * handle line empty context switches * guard against endless loops * see https://phabricator.kde.org/D18509 */ int endlessLoopingCounter = 0; while (!stateData->topContext()->lineEmptyContext().isStay()) { if (!d->switchContext(stateData, stateData->topContext()->lineEmptyContext(), QStringList())) break; // guard against endless loops ++endlessLoopingCounter; if (endlessLoopingCounter > 1024) { qCDebug(Log) << "Endless switch context transitions for line empty context, aborting highlighting of line."; break; } } auto context = stateData->topContext(); applyFormat(0, 0, context->attributeFormat()); return newState; } int offset = 0, beginOffset = 0; bool lineContinuation = false; QHash skipOffsets; /** * current active format * stored as pointer to avoid deconstruction/constructions inside the internal loop * the pointers are stable, the formats are either in the contexts or rules */ auto currentFormat = &stateData->topContext()->attributeFormat(); /** * cached first non-space character, needs to be computed if < 0 */ int firstNonSpace = -1; int lastOffset = offset; int endlessLoopingCounter = 0; do { /** * avoid that we loop endless for some broken hl definitions */ if (lastOffset == offset) { ++endlessLoopingCounter; if (endlessLoopingCounter > 1024) { qCDebug(Log) << "Endless state transitions, aborting highlighting of line."; break; } } else { // ensure we made progress, clear the endlessLoopingCounter Q_ASSERT(offset > lastOffset); lastOffset = offset; endlessLoopingCounter = 0; } /** * try to match all rules in the context in order of declaration in XML */ bool isLookAhead = false; int newOffset = 0; const Format *newFormat = nullptr; for (const auto &rule : stateData->topContext()->rules()) { /** * filter out rules that require a specific column */ if ((rule->requiredColumn() >= 0) && (rule->requiredColumn() != offset)) { continue; } /** * filter out rules that only match for leading whitespace */ if (rule->firstNonSpace()) { /** * compute the first non-space lazy * avoids computing it for contexts without any such rules */ if (firstNonSpace < 0) { firstNonSpace = firstNonSpaceChar(text); } /** * can we skip? */ if (offset > firstNonSpace) { continue; } } /** * shall we skip application of this rule? two cases: * - rule can't match at all => currentSkipOffset < 0 * - rule will only match for some higher offset => currentSkipOffset > offset */ const auto currentSkipOffset = skipOffsets.value(rule.get()); if (currentSkipOffset < 0 || currentSkipOffset > offset) continue; const auto newResult = rule->doMatch(text, offset, stateData->topCaptures()); newOffset = newResult.offset(); /** * update skip offset if new one rules out any later match or is larger than current one */ if (newResult.skipOffset() < 0 || newResult.skipOffset() > currentSkipOffset) skipOffsets.insert(rule.get(), newResult.skipOffset()); if (newOffset <= offset) continue; - // apply folding - if (rule->endRegion().isValid()) + /** + * apply folding. + * special cases: + * - rule with endRegion + beginRegion: in endRegion, the length is 0 + * - rule with lookAhead: length is 0 + */ + if (rule->endRegion().isValid() && rule->beginRegion().isValid()) + applyFolding(offset, 0, rule->endRegion()); + else if (rule->endRegion().isValid()) applyFolding(offset, rule->isLookAhead() ? 0 : newOffset - offset, rule->endRegion()); if (rule->beginRegion().isValid()) applyFolding(offset, rule->isLookAhead() ? 0 : newOffset - offset, rule->beginRegion()); if (rule->isLookAhead()) { Q_ASSERT(!rule->context().isStay()); d->switchContext(stateData, rule->context(), newResult.captures()); isLookAhead = true; break; } d->switchContext(stateData, rule->context(), newResult.captures()); newFormat = rule->attributeFormat().isValid() ? &rule->attributeFormat() : &stateData->topContext()->attributeFormat(); if (newOffset == text.size() && std::dynamic_pointer_cast(rule)) lineContinuation = true; break; } if (isLookAhead) continue; if (newOffset <= offset) { // no matching rule if (stateData->topContext()->fallthrough()) { d->switchContext(stateData, stateData->topContext()->fallthroughContext(), QStringList()); continue; } newOffset = offset + 1; newFormat = &stateData->topContext()->attributeFormat(); } /** * if we arrive here, some new format has to be set! */ Q_ASSERT(newFormat); /** * on format change, apply the last one and switch to new one */ if (newFormat != currentFormat && newFormat->id() != currentFormat->id()) { if (offset > 0) applyFormat(beginOffset, offset - beginOffset, *currentFormat); beginOffset = offset; currentFormat = newFormat; } /** * we must have made progress if we arrive here! */ Q_ASSERT(newOffset > offset); offset = newOffset; } while (offset < text.size()); /** * apply format for remaining text, if any */ if (beginOffset < offset) applyFormat(beginOffset, text.size() - beginOffset, *currentFormat); /** * handle line end context switches * guard against endless loops * see https://phabricator.kde.org/D18509 */ { int endlessLoopingCounter = 0; while (!stateData->topContext()->lineEndContext().isStay() && !lineContinuation) { if (!d->switchContext(stateData, stateData->topContext()->lineEndContext(), QStringList())) break; // guard against endless loops ++endlessLoopingCounter; if (endlessLoopingCounter > 1024) { qCDebug(Log) << "Endless switch context transitions for line end context, aborting highlighting of line."; break; } } } return newState; } bool AbstractHighlighterPrivate::switchContext(StateData *data, const ContextSwitch &contextSwitch, const QStringList &captures) { // kill as many items as requested from the stack, will always keep the initial context alive! const bool initialContextSurvived = data->pop(contextSwitch.popCount()); // if we have a new context to add, push it // then we always "succeed" if (contextSwitch.context()) { data->push(contextSwitch.context(), captures); return true; } // else we abort, if we did try to pop the initial context return initialContextSurvived; } void AbstractHighlighter::applyFolding(int offset, int length, FoldingRegion region) { Q_UNUSED(offset); Q_UNUSED(length); Q_UNUSED(region); }