@@ -36,7 +36,6 @@ class Reline::LineEditor
3636
3737 module CompletionState
3838 NORMAL = :normal
39- COMPLETION = :completion
4039 MENU = :menu
4140 MENU_WITH_PERFECT_MATCH = :menu_with_perfect_match
4241 PERFECT_MATCH = :perfect_match
@@ -800,105 +799,74 @@ def editing_mode
800799 @config . editing_mode
801800 end
802801
803- private def menu ( _target , list )
802+ private def menu ( list )
804803 @menu_info = MenuInfo . new ( list )
805804 end
806805
807- private def complete_internal_proc ( list , is_menu )
808- preposing , target , postposing = retrieve_completion_block
809- candidates = list . select { |i |
810- if i and not Encoding . compatible? ( target . encoding , i . encoding )
811- raise Encoding ::CompatibilityError , "#{ target . encoding . name } is not compatible with #{ i . encoding . name } "
806+ private def filter_normalize_candidates ( target , list )
807+ target = target . downcase if @config . completion_ignore_case
808+ list . select do |item |
809+ next unless item
810+
811+ if Encoding . compatible? ( target . encoding , item . encoding )
812+ # Crash with Encoding::CompatibilityError is required by readline-ext/test/readline/test_readline.rb
813+ # TODO: fix the test
814+ raise Encoding ::CompatibilityError , "#{ target . encoding . name } is not compatible with #{ item . encoding . name } "
812815 end
816+
813817 if @config . completion_ignore_case
814- i & .downcase & .start_with? ( target . downcase )
818+ item . downcase . start_with? ( target )
815819 else
816- i &.start_with? ( target )
817- end
818- } . uniq
819- if is_menu
820- menu ( target , candidates )
821- return nil
822- end
823- completed = candidates . inject { |memo , item |
824- begin
825- memo_mbchars = memo . unicode_normalize . grapheme_clusters
826- item_mbchars = item . unicode_normalize . grapheme_clusters
827- rescue Encoding ::CompatibilityError
828- memo_mbchars = memo . grapheme_clusters
829- item_mbchars = item . grapheme_clusters
830- end
831- size = [ memo_mbchars . size , item_mbchars . size ] . min
832- result = +''
833- size . times do |i |
834- if @config . completion_ignore_case
835- if memo_mbchars [ i ] . casecmp? ( item_mbchars [ i ] )
836- result << memo_mbchars [ i ]
837- else
838- break
839- end
840- else
841- if memo_mbchars [ i ] == item_mbchars [ i ]
842- result << memo_mbchars [ i ]
843- else
844- break
845- end
846- end
820+ item . start_with? ( target )
847821 end
848- result
849- }
850-
851- [ target , preposing , completed , postposing , candidates ]
822+ end . filter_map do |item |
823+ item . unicode_normalize
824+ rescue Encoding ::CompatibilityError
825+ item
826+ end . uniq
852827 end
853828
854- private def perform_completion ( list , just_show_list )
829+ private def perform_completion ( list )
830+ preposing , target , postposing = retrieve_completion_block
831+ candidates = filter_normalize_candidates ( target , list )
832+
855833 case @completion_state
856- when CompletionState ::NORMAL
857- @completion_state = CompletionState ::COMPLETION
858834 when CompletionState ::PERFECT_MATCH
859835 if @dig_perfect_match_proc
860- @dig_perfect_match_proc . ( @perfect_matched )
861- else
862- @completion_state = CompletionState ::COMPLETION
836+ @dig_perfect_match_proc . call ( @perfect_matched )
837+ return
863838 end
864- end
865- if just_show_list
866- is_menu = true
867- elsif @completion_state == CompletionState ::MENU
868- is_menu = true
869- elsif @completion_state == CompletionState ::MENU_WITH_PERFECT_MATCH
870- is_menu = true
871- else
872- is_menu = false
873- end
874- result = complete_internal_proc ( list , is_menu )
875- if @completion_state == CompletionState ::MENU_WITH_PERFECT_MATCH
839+ when CompletionState ::MENU
840+ menu ( candidates )
841+ return
842+ when CompletionState ::MENU_WITH_PERFECT_MATCH
843+ menu ( candidates )
876844 @completion_state = CompletionState ::PERFECT_MATCH
845+ return
877846 end
878- return if result . nil?
879- target , preposing , completed , postposing , candidates = result
880- return if completed . nil?
881- if target <= completed and ( @completion_state == CompletionState ::COMPLETION )
882- append_character = ''
883- if candidates . include? ( completed )
884- if candidates . one?
885- append_character = completion_append_character . to_s
886- @completion_state = CompletionState ::PERFECT_MATCH
887- else
888- @completion_state = CompletionState ::MENU_WITH_PERFECT_MATCH
889- perform_completion ( candidates , true ) if @config . show_all_if_ambiguous
890- end
891- @perfect_matched = completed
847+
848+ completed = Reline ::Unicode . common_prefix ( candidates , ignore_case : @config . completion_ignore_case )
849+ return if completed . empty?
850+
851+ append_character = ''
852+ if candidates . include? ( completed )
853+ if candidates . one?
854+ append_character = completion_append_character . to_s
855+ @completion_state = CompletionState ::PERFECT_MATCH
856+ elsif @config . show_all_if_ambiguous
857+ menu ( candidates )
858+ @completion_state = CompletionState ::PERFECT_MATCH
892859 else
893- @completion_state = CompletionState ::MENU
894- perform_completion ( candidates , true ) if @config . show_all_if_ambiguous
895- end
896- unless just_show_list
897- @buffer_of_lines [ @line_index ] = ( preposing + completed + append_character + postposing ) . split ( "\n " ) [ @line_index ] || String . new ( encoding : encoding )
898- line_to_pointer = ( preposing + completed + append_character ) . split ( "\n " ) [ @line_index ] || String . new ( encoding : encoding )
899- @byte_pointer = line_to_pointer . bytesize
860+ @completion_state = CompletionState ::MENU_WITH_PERFECT_MATCH
900861 end
862+ @perfect_matched = completed
863+ else
864+ @completion_state = CompletionState ::MENU
865+ menu ( candidates ) if @config . show_all_if_ambiguous
901866 end
867+ @buffer_of_lines [ @line_index ] = ( preposing + completed + append_character + postposing ) . split ( "\n " ) [ @line_index ] || String . new ( encoding : encoding )
868+ line_to_pointer = ( preposing + completed + append_character ) . split ( "\n " ) [ @line_index ] || String . new ( encoding : encoding )
869+ @byte_pointer = line_to_pointer . bytesize
902870 end
903871
904872 def dialog_proc_scope_completion_journey_data
@@ -1463,7 +1431,7 @@ def finish
14631431 result = call_completion_proc
14641432 if result . is_a? ( Array )
14651433 @completion_occurs = true
1466- perform_completion ( result , false )
1434+ perform_completion ( result )
14671435 end
14681436 end
14691437 end
@@ -1929,7 +1897,9 @@ def finish
19291897 elsif !@config . autocompletion # show completed list
19301898 result = call_completion_proc
19311899 if result . is_a? ( Array )
1932- perform_completion ( result , true )
1900+ _preposing , target = retrieve_completion_block
1901+ candidates = filter_normalize_candidates ( target , result )
1902+ menu ( candidates )
19331903 end
19341904 end
19351905 end
0 commit comments