@@ -166,8 +166,12 @@ contains |
| 166 | 166 | do while (i <= line_len) |
| 167 | 167 | ch = line(i:i) |
| 168 | 168 | |
| 169 | + ! Check for multiline comment start |
| 170 | + if (check_multiline_comment_start(highlighter, line, i)) then |
| 171 | + call process_multiline_comment_start(highlighter, line, tokens, token_count, i) |
| 172 | + |
| 169 | 173 | ! Check for single-line comment |
| 170 | | - if (check_comment_start(highlighter, line, i)) then |
| 174 | + else if (check_comment_start(highlighter, line, i)) then |
| 171 | 175 | token_count = token_count + 1 |
| 172 | 176 | tokens(token_count)%type = TOKEN_COMMENT |
| 173 | 177 | tokens(token_count)%start_col = i |
@@ -396,6 +400,22 @@ contains |
| 396 | 400 | end if |
| 397 | 401 | end function check_comment_start |
| 398 | 402 | |
| 403 | + function check_multiline_comment_start(highlighter, line, pos) result(res) |
| 404 | + type(syntax_highlighter_t), intent(in) :: highlighter |
| 405 | + character(len=*), intent(in) :: line |
| 406 | + integer, intent(in) :: pos |
| 407 | + logical :: res |
| 408 | + integer :: comment_len |
| 409 | + |
| 410 | + res = .false. |
| 411 | + if (highlighter%current_lang%comment_start /= "") then |
| 412 | + comment_len = len_trim(highlighter%current_lang%comment_start) |
| 413 | + if (pos + comment_len - 1 <= len(line)) then |
| 414 | + res = line(pos:pos+comment_len-1) == trim(highlighter%current_lang%comment_start) |
| 415 | + end if |
| 416 | + end if |
| 417 | + end function check_multiline_comment_start |
| 418 | + |
| 399 | 419 | function check_string_start(highlighter, line, pos) result(res) |
| 400 | 420 | type(syntax_highlighter_t), intent(in) :: highlighter |
| 401 | 421 | character(len=*), intent(in) :: line |
@@ -424,7 +444,7 @@ contains |
| 424 | 444 | integer, intent(inout) :: token_count, pos |
| 425 | 445 | integer :: i, start_pos, delim_len, line_len |
| 426 | 446 | character(len=:), allocatable :: delimiter |
| 427 | | - logical :: found_end |
| 447 | + logical :: found_end, is_multiline |
| 428 | 448 | |
| 429 | 449 | line_len = len(line) |
| 430 | 450 | start_pos = pos |
@@ -443,6 +463,11 @@ contains |
| 443 | 463 | end if |
| 444 | 464 | end do |
| 445 | 465 | |
| 466 | + ! Check if this is a multiline-capable delimiter |
| 467 | + ! Python: """ or ''' (length 3) |
| 468 | + ! JavaScript/TypeScript: ` (template literals) |
| 469 | + is_multiline = (len(delimiter) >= 3) .or. (delimiter == '`') |
| 470 | + |
| 446 | 471 | ! Move past opening delimiter |
| 447 | 472 | pos = pos + len(delimiter) |
| 448 | 473 | |
@@ -473,6 +498,11 @@ contains |
| 473 | 498 | |
| 474 | 499 | ! Handle unclosed string |
| 475 | 500 | if (.not. found_end) then |
| 501 | + if (is_multiline) then |
| 502 | + ! Enter multiline string mode |
| 503 | + highlighter%in_multiline_string = .true. |
| 504 | + highlighter%string_delimiter = delimiter |
| 505 | + end if |
| 476 | 506 | pos = line_len + 1 |
| 477 | 507 | end if |
| 478 | 508 | end subroutine process_string |
@@ -949,13 +979,83 @@ contains |
| 949 | 979 | highlighter%enabled = .true. |
| 950 | 980 | end subroutine load_markdown_syntax |
| 951 | 981 | |
| 952 | | - ! Stub implementations for multiline handling |
| 982 | + ! Handle multiline comment start (when we encounter /* on a line) |
| 983 | + subroutine process_multiline_comment_start(highlighter, line, tokens, token_count, pos) |
| 984 | + type(syntax_highlighter_t), intent(inout) :: highlighter |
| 985 | + character(len=*), intent(in) :: line |
| 986 | + type(token_t), intent(inout) :: tokens(:) |
| 987 | + integer, intent(inout) :: token_count, pos |
| 988 | + integer :: start_pos, end_pos, comment_start_len, comment_end_len, line_len |
| 989 | + |
| 990 | + line_len = len(line) |
| 991 | + start_pos = pos |
| 992 | + comment_start_len = len_trim(highlighter%current_lang%comment_start) |
| 993 | + comment_end_len = len_trim(highlighter%current_lang%comment_end) |
| 994 | + |
| 995 | + ! Skip past the opening delimiter |
| 996 | + pos = pos + comment_start_len |
| 997 | + |
| 998 | + ! Look for closing delimiter on the same line |
| 999 | + end_pos = index(line(pos:), trim(highlighter%current_lang%comment_end)) |
| 1000 | + |
| 1001 | + if (end_pos > 0) then |
| 1002 | + ! Found closing delimiter on same line - this is a complete comment |
| 1003 | + end_pos = pos + end_pos - 1 + comment_end_len - 1 |
| 1004 | + |
| 1005 | + token_count = token_count + 1 |
| 1006 | + tokens(token_count)%type = TOKEN_COMMENT |
| 1007 | + tokens(token_count)%start_col = start_pos |
| 1008 | + tokens(token_count)%end_col = end_pos |
| 1009 | + |
| 1010 | + pos = end_pos + 1 |
| 1011 | + else |
| 1012 | + ! Closing delimiter not found - rest of line is comment, enter multiline mode |
| 1013 | + token_count = token_count + 1 |
| 1014 | + tokens(token_count)%type = TOKEN_COMMENT |
| 1015 | + tokens(token_count)%start_col = start_pos |
| 1016 | + tokens(token_count)%end_col = line_len |
| 1017 | + |
| 1018 | + highlighter%in_multiline_comment = .true. |
| 1019 | + pos = line_len + 1 |
| 1020 | + end if |
| 1021 | + end subroutine process_multiline_comment_start |
| 1022 | + |
| 1023 | + ! Handle multiline comment continuation (when we start a line in comment mode) |
| 953 | 1024 | subroutine process_multiline_comment(highlighter, line, tokens, token_count, pos) |
| 954 | 1025 | type(syntax_highlighter_t), intent(inout) :: highlighter |
| 955 | 1026 | character(len=*), intent(in) :: line |
| 956 | 1027 | type(token_t), intent(inout) :: tokens(:) |
| 957 | 1028 | integer, intent(inout) :: token_count, pos |
| 958 | | - ! TODO: Implement multiline comment handling |
| 1029 | + integer :: end_pos, comment_end_len, line_len |
| 1030 | + |
| 1031 | + line_len = len(line) |
| 1032 | + comment_end_len = len_trim(highlighter%current_lang%comment_end) |
| 1033 | + |
| 1034 | + ! Look for closing delimiter |
| 1035 | + end_pos = index(line(pos:), trim(highlighter%current_lang%comment_end)) |
| 1036 | + |
| 1037 | + if (end_pos > 0) then |
| 1038 | + ! Found closing delimiter on this line |
| 1039 | + end_pos = pos + end_pos - 1 + comment_end_len - 1 |
| 1040 | + |
| 1041 | + token_count = token_count + 1 |
| 1042 | + tokens(token_count)%type = TOKEN_COMMENT |
| 1043 | + tokens(token_count)%start_col = pos |
| 1044 | + tokens(token_count)%end_col = end_pos |
| 1045 | + |
| 1046 | + ! Exit multiline comment mode |
| 1047 | + highlighter%in_multiline_comment = .false. |
| 1048 | + pos = end_pos + 1 |
| 1049 | + else |
| 1050 | + ! Closing delimiter not found - entire rest of line is comment |
| 1051 | + token_count = token_count + 1 |
| 1052 | + tokens(token_count)%type = TOKEN_COMMENT |
| 1053 | + tokens(token_count)%start_col = pos |
| 1054 | + tokens(token_count)%end_col = line_len |
| 1055 | + |
| 1056 | + ! Stay in multiline comment mode |
| 1057 | + pos = line_len + 1 |
| 1058 | + end if |
| 959 | 1059 | end subroutine process_multiline_comment |
| 960 | 1060 | |
| 961 | 1061 | subroutine process_multiline_string(highlighter, line, tokens, token_count, pos) |
@@ -963,7 +1063,37 @@ contains |
| 963 | 1063 | character(len=*), intent(in) :: line |
| 964 | 1064 | type(token_t), intent(inout) :: tokens(:) |
| 965 | 1065 | integer, intent(inout) :: token_count, pos |
| 966 | | - ! TODO: Implement multiline string handling |
| 1066 | + integer :: end_pos, delim_len, line_len |
| 1067 | + |
| 1068 | + line_len = len(line) |
| 1069 | + delim_len = len(highlighter%string_delimiter) |
| 1070 | + |
| 1071 | + ! Look for closing delimiter |
| 1072 | + end_pos = index(line(pos:), highlighter%string_delimiter) |
| 1073 | + |
| 1074 | + if (end_pos > 0) then |
| 1075 | + ! Found closing delimiter on this line |
| 1076 | + end_pos = pos + end_pos - 1 + delim_len - 1 |
| 1077 | + |
| 1078 | + token_count = token_count + 1 |
| 1079 | + tokens(token_count)%type = TOKEN_STRING |
| 1080 | + tokens(token_count)%start_col = pos |
| 1081 | + tokens(token_count)%end_col = end_pos |
| 1082 | + |
| 1083 | + ! Exit multiline string mode |
| 1084 | + highlighter%in_multiline_string = .false. |
| 1085 | + highlighter%string_delimiter = "" |
| 1086 | + pos = end_pos + 1 |
| 1087 | + else |
| 1088 | + ! Closing delimiter not found - entire rest of line is string |
| 1089 | + token_count = token_count + 1 |
| 1090 | + tokens(token_count)%type = TOKEN_STRING |
| 1091 | + tokens(token_count)%start_col = pos |
| 1092 | + tokens(token_count)%end_col = line_len |
| 1093 | + |
| 1094 | + ! Stay in multiline string mode |
| 1095 | + pos = line_len + 1 |
| 1096 | + end if |
| 967 | 1097 | end subroutine process_multiline_string |
| 968 | 1098 | |
| 969 | 1099 | end module syntax_highlighter_module |