@@ -87,12 +87,12 @@ function get_style_rules() result(rules)
8787 ! F003: Line too long
8888 rule% code = " F003"
8989 rule% name = " line-too-long"
90- rule% description = " Line exceeds maximum length"
90+ rule% description = " Line exceeds maximum length with suggested breaking points "
9191 rule% category = CATEGORY_STYLE
9292 rule% subcategory = " formatting"
9393 rule% default_enabled = .true.
94- rule% fixable = .false .
95- rule% severity = SEVERITY_INFO
94+ rule% fixable = .true .
95+ rule% severity = SEVERITY_WARNING
9696 rule% check = > check_f003_line_length
9797 ! Registration handled by caller
9898
@@ -273,12 +273,12 @@ function get_style_rules() result(rules)
273273 ! F003
274274 rules(3 )% code = " F003"
275275 rules(3 )% name = " line-too-long"
276- rules(3 )% description = " Line exceeds maximum length"
276+ rules(3 )% description = " Line exceeds maximum length with suggested breaking points "
277277 rules(3 )% category = CATEGORY_STYLE
278278 rules(3 )% subcategory = " formatting"
279279 rules(3 )% default_enabled = .true.
280- rules(3 )% fixable = .false .
281- rules(3 )% severity = SEVERITY_INFO
280+ rules(3 )% fixable = .true .
281+ rules(3 )% severity = SEVERITY_WARNING
282282 rules(3 )% check = > check_f003_line_length
283283
284284 ! F004
@@ -923,6 +923,139 @@ subroutine analyze_line_lengths_from_text(source_text, violations, violation_cou
923923
924924 end subroutine analyze_line_lengths_from_text
925925
926+ ! Enhanced line length analysis with AST context for smart breaking points
927+ subroutine analyze_line_lengths_with_ast_context (ctx , source_text , violations , violation_count , max_length )
928+ type (fluff_ast_context_t), intent (in ) :: ctx
929+ character (len=* ), intent (in ) :: source_text
930+ type (diagnostic_t), intent (inout ) :: violations(:)
931+ integer , intent (inout ) :: violation_count
932+ integer , intent (in ) :: max_length
933+
934+ character (len= 1000 ) :: line
935+ integer :: pos, next_pos, line_num
936+ integer :: line_length
937+ type (source_range_t) :: location
938+ type (fix_suggestion_t), allocatable :: fix_suggestions(:)
939+ character (len= :), allocatable :: suggested_fix
940+ type (text_edit_t) :: text_edit
941+
942+ pos = 1
943+ line_num = 0
944+
945+ do while (pos <= len (source_text))
946+ ! Find end of line
947+ next_pos = index (source_text(pos:), char (10 ))
948+ if (next_pos == 0 ) then
949+ line = source_text(pos:)
950+ pos = len (source_text) + 1
951+ else
952+ line = source_text(pos:pos+ next_pos-2 )
953+ pos = pos + next_pos
954+ end if
955+
956+ line_num = line_num + 1
957+ line_length = len_trim (line)
958+
959+ ! Check if line exceeds maximum length and is not a comment
960+ if (line_length > max_length .and. .not. is_comment_line(line)) then
961+ violation_count = violation_count + 1
962+
963+ ! Create location
964+ location% start% line = line_num
965+ location% start% column = max_length + 1
966+ location% end% line = line_num
967+ location% end% column = line_length
968+
969+ ! Generate smart breaking suggestions based on AST context
970+ call generate_line_break_suggestions(line, suggested_fix)
971+
972+ ! Create text edit for the fix
973+ text_edit% range = location
974+ text_edit% new_text = suggested_fix
975+
976+ ! Create fix suggestion
977+ allocate (fix_suggestions(1 ))
978+ fix_suggestions(1 ) = create_fix_suggestion( &
979+ description= " Break line at logical points" , &
980+ edits= [text_edit])
981+
982+ violations(violation_count) = create_diagnostic( &
983+ code= " F003" , &
984+ message= " Line too long (" // trim (adjustl (int_to_str(line_length))) // &
985+ " > " // trim (adjustl (int_to_str(max_length))) // " characters)" , &
986+ file_path= current_filename, &
987+ location= location, &
988+ severity= SEVERITY_WARNING)
989+
990+ ! Add fix suggestions to the diagnostic
991+ allocate (violations(violation_count)% fixes, source= fix_suggestions)
992+ end if
993+ end do
994+
995+ end subroutine analyze_line_lengths_with_ast_context
996+
997+ ! Generate smart line breaking suggestions
998+ subroutine generate_line_break_suggestions (line , suggested_fix )
999+ character (len=* ), intent (in ) :: line
1000+ character (len= :), allocatable , intent (out ) :: suggested_fix
1001+
1002+ character (len= :), allocatable :: trimmed_line
1003+ integer :: break_pos, i
1004+ logical :: found_break_point
1005+
1006+ trimmed_line = trim (line)
1007+ found_break_point = .false.
1008+
1009+ ! Look for good breaking points (in order of preference):
1010+ ! 1. After commas in argument lists
1011+ ! 2. After operators (+, -, *, /, ==, etc.)
1012+ ! 3. After :: in declarations
1013+ ! 4. Before keywords (then, else, etc.)
1014+
1015+ ! First try: break after commas
1016+ do i = len (trimmed_line), 50 , - 1 ! Start from end, work backwards to column 50
1017+ if (i <= len (trimmed_line) .and. trimmed_line(i:i) == ' ,' ) then
1018+ break_pos = i
1019+ found_break_point = .true.
1020+ exit
1021+ end if
1022+ end do
1023+
1024+ ! Second try: break after operators if no comma found
1025+ if (.not. found_break_point) then
1026+ do i = len (trimmed_line), 50 , - 1
1027+ if (i <= len (trimmed_line)) then
1028+ if (trimmed_line(i:i) == ' +' .or. trimmed_line(i:i) == ' -' .or. &
1029+ trimmed_line(i:i) == ' *' .or. trimmed_line(i:i) == ' /' ) then
1030+ break_pos = i
1031+ found_break_point = .true.
1032+ exit
1033+ end if
1034+ end if
1035+ end do
1036+ end if
1037+
1038+ ! Third try: break after ::
1039+ if (.not. found_break_point) then
1040+ i = index (trimmed_line, ' ::' )
1041+ if (i > 0 .and. i < 80 ) then
1042+ break_pos = i + 1
1043+ found_break_point = .true.
1044+ end if
1045+ end if
1046+
1047+ ! Generate the suggested fix
1048+ if (found_break_point) then
1049+ suggested_fix = trimmed_line(1 :break_pos) // " &" // new_line(' a' ) // &
1050+ " " // trim (adjustl (trimmed_line(break_pos+1 :)))
1051+ else
1052+ ! Fallback: simple break at 80 characters
1053+ suggested_fix = trimmed_line(1 :80 ) // " &" // new_line(' a' ) // &
1054+ " " // trim (adjustl (trimmed_line(81 :)))
1055+ end if
1056+
1057+ end subroutine generate_line_break_suggestions
1058+
9261059 ! Helper function to check if line is a comment
9271060 function is_comment_line (line ) result(is_comment)
9281061 character (len=* ), intent (in ) :: line
@@ -2490,31 +2623,28 @@ subroutine check_indentation_consistency(indent_levels, line_count, violations,
24902623 end if
24912624 end subroutine check_indentation_consistency
24922625
2493- ! F003: Check line length
2626+ ! F003: Check line length with AST context awareness
24942627 subroutine check_f003_line_length (ctx , node_index , violations )
24952628 type (fluff_ast_context_t), intent (in ) :: ctx
24962629 integer , intent (in ) :: node_index
24972630 type (diagnostic_t), allocatable , intent (out ) :: violations(:)
24982631
24992632 type (diagnostic_t), allocatable :: temp_violations(:)
25002633 integer :: violation_count
2501- character (len= 1000 ) :: source_line
2502- integer :: line_num, line_length
25032634 integer , parameter :: MAX_LINE_LENGTH = 88 ! Following Black/Ruff standard
2504- type (source_range_t) :: location
25052635
25062636 ! Initialize
25072637 allocate (temp_violations(100 ))
25082638 violation_count = 0
25092639
2510- ! Use current_source_text to check line lengths
2640+ ! Use current_source_text to check line lengths with AST context
25112641 if (.not. allocated (current_source_text)) then
25122642 ! No source text available
25132643 allocate (violations(0 ))
25142644 return
25152645 end if
25162646
2517- call analyze_line_lengths_from_text( current_source_text, temp_violations, violation_count, MAX_LINE_LENGTH)
2647+ call analyze_line_lengths_with_ast_context(ctx, current_source_text, temp_violations, violation_count, MAX_LINE_LENGTH)
25182648
25192649 ! Allocate result
25202650 allocate (violations(violation_count))
0 commit comments