Hiroyuki Komatsu
komat****@users*****
2004年 12月 9日 (木) 16:05:36 JST
Index: suikyo/ruby/src/suikyo-composer.rb diff -u suikyo/ruby/src/suikyo-composer.rb:1.2 suikyo/ruby/src/suikyo-composer.rb:1.3 --- suikyo/ruby/src/suikyo-composer.rb:1.2 Mon Dec 6 00:02:04 2004 +++ suikyo/ruby/src/suikyo-composer.rb Thu Dec 9 16:05:36 2004 @@ -1,6 +1,6 @@ # suikyo-composer.rb: Preedition composer using Suikyo. # This library is for input methods such as PRIME. -# $Id: suikyo-composer.rb,v 1.2 2004/12/05 15:02:04 komatsu Exp $ +# $Id: suikyo-composer.rb,v 1.3 2004/12/09 07:05:36 komatsu Exp $ # # Copyright (C) 2004 Hiroyuki Komatsu <komat****@taiya*****> # All rights reserved. @@ -45,16 +45,16 @@ end class SuikyoComposerData - attr_reader :position, :head_chunk, :tail_chunk, :surface_mode, :typing_mode - def initialize (position, head_chunk, tail_chunk, surface_mode, typing_mode) + attr_reader :position, :head_chunk, :tail_chunk, :typing_mode + def initialize (position, head_chunk, tail_chunk, typing_mode) @position = position @head_chunk = head_chunk @tail_chunk = tail_chunk - @surface_mode = surface_mode - @typing_mode = typing_mode + @typing_mode = typing_mode end end +## Main class of this file. class SuikyoComposer @@suikyo_katakana = Suikyo.new("hiragana-katakana") @@suikyo_half_katakana = Suikyo.new("hiragana-halfkatakana") @@ -63,17 +63,27 @@ @@suikyo_half_katakana_reverse = Suikyo.new("halfkatakana-hiragana") @@suikyo_wide_ascii_reverse = Suikyo.new("wideascii-ascii") + attr_accessor :mask, :hybrid_typing def initialize (table = nil, table_reverse = nil) @suikyo = Suikyo.new(table) @suikyo_reverse = Suikyo.new(table_reverse) reset_composer() - @surface_mode = :default # :raw, :katakana, :wide_ascii, :half_katakana - @typing_mode = :default # :raw + ## @typing_mode means the current displaying method. Its value is + ## one of :default, :raw, :katakana, :wide_ascii, and :half_katakana. + @typing_mode = :default @undo_data = [] @redo_data = [] # not implemented yet. + + ## If @mask is true, the surface of tail pending character will be masked + ## by "*". For example "たいy" => "たい*". This is usually for T-code. + @mask = false + + ## If @hybrid_typing is true, Suikyo considers the validation of the + ## preedition. ex). "あっplえ" => "apple". + @hybrid_typing = true end def reset_composer () @@ -100,7 +110,7 @@ ## This method sets the current status to the undo buffer. def undo_set () data = SuikyoComposerData.new(@position, @head_chunk, @tail_chunk, - @surface_mode, @typing_mode) + @typing_mode) # @undo_data.push(data) @undo_data = [data] end @@ -120,8 +130,7 @@ @position = data.position @head_chunk = data.head_chunk @tail_chunk = data.tail_chunk - @surface_mode = data.surface_mode - @typing_mode = data.typing_mode + @typing_mode = data.typing_mode return true end @@ -197,9 +206,9 @@ end case @typing_mode - when :default then + when :default, :katakana, :half_katakana then @position = edit_backspace_internal_default(chunk) - when :raw then + else # :raw, :wide_ascii @position = edit_backspace_internal_raw(chunk) end return @position @@ -266,20 +275,15 @@ def edit_display () chunk = @head_chunk detail = "" - surface = "" until chunk.chunk_next == @tail_chunk do chunk = chunk.chunk_next original = chunk.original.nil? ? "*" : chunk.original.join() detail += "[#{chunk.conversion.join}+#{chunk.pending.join}|#{original}]" - surface += chunk_get_surface(chunk) end puts(detail) - chars = surface.split(//) - if chars.length == 0 then - puts("|") - else - puts(chars[0, @ position].join + "|" + chars[@position..-1].join) - end + + (left, cursor, right) = edit_get_preediting_string() + puts(left + "|" + cursor + right) end def get_position (chunk, offset = nil) @@ -321,12 +325,81 @@ return surface end + ## This method returns a translated string under the current mode. + ## If @mask is true, pending characters are masked by "*". + def edit_get_surface_string_masked () + chunk = @head_chunk + surface = "" + until chunk.chunk_next == @tail_chunk do + chunk = chunk.chunk_next + surface += chunk_get_surface_masked(chunk) + end + return surface + end + + ## This checks the validation of the preediting string. If the preedition + ## is valid, it returns true. If invalid, false. + ## ex). "apple (あっplえ)" => false, "ringo(りんご)" => true. + def hybrid_typing_check_validation () + chunk = @head_chunk + (current_chunk, offset) = chunk_get_at(@position) + + flag_valid = true + flag_original = true + + until chunk.chunk_next == @tail_chunk do + chunk = chunk.chunk_next + + ## If pending characters exist in the preediting chunks except the last + ## chunk, the flag for validation becomes false. + if (chunk.pending.length > 0 and + chunk.chunk_next != @tail_chunk and chunk != current_chunk) or \ + (chunk.pending.length + chunk.conversion.length == 0) then + flag_valid = false + end + + ## If there're chunks whose original value is nil, the flag for original + ## input becomes false. + if chunk.original.nil? then + flag_original = false + end + end + + if flag_valid == false and flag_original == true then + return false + end + return true + end + + def hybrid_typing_update () + unless @hybrid_typing then + return @typing_mode + end + + new_mode = hybrid_typing_check_validation() ? :default : :raw + + if new_mode == @typing_mode then + return @typing_mode + end + + if new_mode == :default then + (chunk, offset) = edit_recover_conversion() + else # @typing_mode == :raw + (chunk, offset) = edit_recover_original() + end + @typing_mode = new_mode + @position = get_position(chunk, offset) + return @typing_mode + end + + ## This method returns a list of a translated string under the current mode. ## The list nodes are: [left-string, cursor-character, right-string] ## ex). "aiu|eo" => ["aiu", "e", "o"] def edit_get_preediting_string () - surface = edit_get_surface_string() - chars = surface.split(//) + hybrid_typing_update() + surface = edit_get_surface_string_masked() + chars = surface.split(//) left_string = chars[0, @ position].join() cursor_string = (chars[@position] or "") @@ -339,7 +412,7 @@ ## Input Method Editor. Basically it returns the original typed string, ## or the displayed string if the original string is broken. def edit_get_query_string () - if @surface_mode != :default then + if @typing_mode != :default then return edit_get_surface_string() end @@ -372,7 +445,7 @@ ## preedition string. For example, in the case the preedition is "sh", ## this returns ["sh", "しゃ", しゅ", "しょ"]. def edit_get_expansion () - if @typing_mode == :raw then + if @typing_mode == :raw or @typing_mode == :wide_ascii then return [ edit_get_surface_string() ] end @@ -417,42 +490,46 @@ return results end + def set_mode_hybrid () + @hybrid_typing = true + hybrid_typing_update() + end + def set_mode_default () # F6 + @hybrid_typing = false (chunk, offset) = edit_recover_conversion() - @typing_mode = :default - @surface_mode = :default + @typing_mode = :default @position = get_position(chunk, offset) end def set_mode_katakana () # F7 + @hybrid_typing = false (chunk, offset) = edit_recover_conversion() - @typing_mode = :default - @surface_mode = :katakana + @typing_mode = :katakana @position = get_position(chunk, offset) end def set_mode_half_katakana () # F8 + @hybrid_typing = false (chunk, offset) = edit_recover_conversion() - @typing_mode = :default - @surface_mode = :half_katakana + @typing_mode = :half_katakana @position = get_position(chunk, offset) end def set_mode_wide_ascii () # F9 + @hybrid_typing = false (chunk, offset) = edit_recover_original() - @typing_mode = :raw - @surface_mode = :wide_ascii + @typing_mode = :wide_ascii @position = get_position(chunk, offset) end def set_mode_raw () # F10 + @hybrid_typing = false (chunk, offset) = edit_recover_original() - @typing_mode = :raw - @surface_mode = :raw + @typing_mode = :raw @position = get_position(chunk, offset) end - private ## This method converts the preedition string again and returns the @@ -467,32 +544,39 @@ offset = nil end - chunk = @head_chunk - pending = "" + chunk = @head_chunk + pending_chunk = nil until chunk.chunk_next == @tail_chunk do - if chunk.chunk_next.conversion.length > 0 then - if pending != "" then - chunk = SuikyoCharChunk.new_next(chunk) - chunk = convert(chunk, pending) + if chunk.chunk_next.conversion.length > 0 or + chunk.chunk_next.pending.length > 0 then + if pending_chunk then + chunk = convert(pending_chunk, "") end chunk = chunk.chunk_next - pending = "" + pending_chunk = nil else # chunk.chunk_next.conversion.length == 0 chunk = chunk.chunk_next - pending += chunk.pending.join() - next_chunk = chunk_delete(chunk) - if cur_chunk == chunk then - cur_chunk = next_chunk - offset = nil + + if pending_chunk then + pending_chunk.pending += chunk.original + next_chunk = chunk_delete(chunk) + + if cur_chunk == chunk then + cur_chunk = next_chunk + offset = nil + end + chunk = next_chunk + else + pending_chunk = chunk + pending_chunk.conversion = [] + pending_chunk.pending = pending_chunk.original.dup end - chunk = next_chunk end end - if pending != "" then - chunk = SuikyoCharChunk.new_next(chunk) - chunk = convert(chunk, pending) + if pending_chunk then + chunk = convert(pending_chunk, "") end return [cur_chunk, offset] end @@ -510,33 +594,41 @@ end chunk = @head_chunk + reconv_chunk = nil reconv = "" until chunk.chunk_next == @tail_chunk do if chunk.chunk_next.original then - if reconv != "" then - input = @suikyo_reverse.convert( reconv + " " ) - chunk = SuikyoCharChunk.new_next(chunk) - chunk = convert(chunk, input) + if reconv_chunk then + input = @suikyo_reverse.convert( reconv_chunk.conversion.join + " " ) + reconv_chunk.conversion = [] + chunk = convert(reconv_chunk, input) end chunk = chunk.chunk_next - reconv = "" + reconv_chunk = nil else - chunk = chunk.chunk_next - reconv += chunk.conversion.join() - next_chunk = chunk_delete(chunk) - if cur_chunk == chunk then - cur_chunk = next_chunk - offset = 0 + chunk = chunk.chunk_next + if reconv_chunk then + reconv_chunk.conversion += chunk.conversion + chunk.pending + next_chunk = chunk_delete(chunk) + if cur_chunk == chunk then + cur_chunk = next_chunk + offset = 0 + end + chunk = next_chunk + else + reconv_chunk = chunk + reconv_chunk.conversion += reconv_chunk.pending + reconv_chunk.pending = [] + reconv_chunk.original = [] end - chunk = next_chunk end end - if reconv != "" then - input = @suikyo_reverse.convert( reconv + " " ) - chunk = SuikyoCharChunk.new_next(chunk) - chunk = convert(chunk, input) + if reconv_chunk then + input = @suikyo_reverse.convert( reconv_chunk.conversion.join + " " ) + reconv_chunk.conversion = [] + chunk = convert(reconv_chunk, input) end return [cur_chunk, offset] @@ -559,17 +651,36 @@ ## This function returns a string of the specified SuikyoChunk.for display. ## The string is depend on the current typing_mode. def chunk_get_surface (chunk) - if @typing_mode == :default then + case @typing_mode + when :default, :katakana, :half_katakana then string = chunk.conversion.join() + chunk.pending.join() - else # @typing_mode == :raw + else # @typing_mode == :raw, :wide-ascii + string = chunk.original.join() + end + return suikyo_convert(string) + end + + ## This is basically the same with chunk_get_surface. The difference is + ## that if @mask is true this method masks pending characters with "*". + def chunk_get_surface_masked (chunk) + case @typing_mode + when :default, :katakana, :half_katakana then + if @mask then + string = chunk.conversion.join() + ("*" * chunk.pending.length()) + else + string = chunk.conversion.join() + chunk.pending.join() + end + else # @typing_mode == :raw, :wide-ascii string = chunk.original.join() end return suikyo_convert(string) end + + ## This returns the result of suikyo.convert in each surface mode. def suikyo_convert (string) - case @surface_mode + case @typing_mode when :default then # F6 retur****@suiky*****(string) when :katakana then # F7 @@ -585,7 +696,7 @@ ## This reconverts a string to the default surface and returns it. def suikyo_reconvert_to_default (string) - case @surface_mode + case @typing_mode when :default then # F6 return string when :katakana then # F7 @@ -600,7 +711,7 @@ end ## This function returns the length of the specified SuikyoChunk. - ## The value is depend on the current surface_mode. + ## The value is depend on the current typing_mode. def chunk_get_length (chunk) return chunk_get_surface(chunk).split(//).length() end @@ -628,61 +739,13 @@ ## and returns true. If it failed, it returns false. def chunk_split (chunk, position) case @typing_mode - when :default then + when :default, :katakana, :half_katakana then return chunk_split_default(chunk, position) - when :raw then + else # :raw, :wide_ascii return chunk_split_raw(chunk, position) end end - def chunk_split_default2 (chunk, position) - puts "chunk_split_default (#{chunk}, #{position})" - puts "chunk_length = #{chunk_get_length(chunk)}" - if position > chunk_get_length(chunk) or position <= 0 then - return false - end - - ### Separate into two chunks - ## Getting the pending and conversion data of left and right chunks. - original = chunk.original - if position > chunk.conversion.length then - position_pending = position - chunk.conversion.length - left_pending = chunk.pending[0,position_pending] - left_conversion = chunk.conversion - right_pending = chunk.pending[position_pending..-1] - right_conversion = [] - else # position <= chunk.conversion.length - left_pending = [] - left_conversion = chunk.conversion[0,position] - right_pending = chunk.pending - right_conversion = chunk.conversion[position..-1] - end - - ## Getting the data original of left and right chunks. - left_original = nil - right_original = nil - if original != nil and right_conversion == [] then - index = original.join.rindex(right_pending.join) - if index != nil then - left_original = original[0,index] - right_original = original[index..-1] - end - end - - ## Reflecting the data to chunks. - chunk.conversion = suikyo_reconvert_to_default( left_conversion ) - chunk.pending = suikyo_reconvert_to_default( left_pending ) - chunk.original = left_original - if right_conversion.length > 0 or right_pending.length > 0 then - right_chunk = SuikyoCharChunk.new_next(chunk) - right_chunk.conversion = suikyo_reconvert_to_default( right_conversion ) - right_chunk.pending = suikyo_reconvert_to_default( right_pending ) - right_chunk.original = right_original - end - - return true - end - def chunk_split_default (chunk, position) if position > chunk_get_length(chunk) or position <= 0 then return false