ime.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. // SPDX-License-Identifier: Unlicense OR MIT
  2. package app
  3. import (
  4. "unicode"
  5. "unicode/utf16"
  6. "gioui.org/io/input"
  7. "gioui.org/io/key"
  8. )
  9. type editorState struct {
  10. input.EditorState
  11. compose key.Range
  12. }
  13. func (e *editorState) Replace(r key.Range, text string) {
  14. if r.Start > r.End {
  15. r.Start, r.End = r.End, r.Start
  16. }
  17. runes := []rune(text)
  18. newEnd := r.Start + len(runes)
  19. adjust := func(pos int) int {
  20. switch {
  21. case newEnd < pos && pos <= r.End:
  22. return newEnd
  23. case r.End < pos:
  24. diff := newEnd - r.End
  25. return pos + diff
  26. }
  27. return pos
  28. }
  29. e.Selection.Start = adjust(e.Selection.Start)
  30. e.Selection.End = adjust(e.Selection.End)
  31. if e.compose.Start != -1 {
  32. e.compose.Start = adjust(e.compose.Start)
  33. e.compose.End = adjust(e.compose.End)
  34. }
  35. s := e.Snippet
  36. if r.End < s.Start || r.Start > s.End {
  37. // Discard snippet if it doesn't overlap with replacement.
  38. s = key.Snippet{
  39. Range: key.Range{
  40. Start: r.Start,
  41. End: r.Start,
  42. },
  43. }
  44. }
  45. var newSnippet []rune
  46. snippet := []rune(s.Text)
  47. // Append first part of existing snippet.
  48. if end := r.Start - s.Start; end > 0 {
  49. newSnippet = append(newSnippet, snippet[:end]...)
  50. }
  51. // Append replacement.
  52. newSnippet = append(newSnippet, runes...)
  53. // Append last part of existing snippet.
  54. if start := r.End; start < s.End {
  55. newSnippet = append(newSnippet, snippet[start-s.Start:]...)
  56. }
  57. // Adjust snippet range to include replacement.
  58. if r.Start < s.Start {
  59. s.Start = r.Start
  60. }
  61. s.End = s.Start + len(newSnippet)
  62. s.Text = string(newSnippet)
  63. e.Snippet = s
  64. }
  65. // UTF16Index converts the given index in runes into an index in utf16 characters.
  66. func (e *editorState) UTF16Index(runes int) int {
  67. if runes == -1 {
  68. return -1
  69. }
  70. if runes < e.Snippet.Start {
  71. // Assume runes before sippet are one UTF-16 character each.
  72. return runes
  73. }
  74. chars := e.Snippet.Start
  75. runes -= e.Snippet.Start
  76. for _, r := range e.Snippet.Text {
  77. if runes == 0 {
  78. break
  79. }
  80. runes--
  81. chars++
  82. if r1, _ := utf16.EncodeRune(r); r1 != unicode.ReplacementChar {
  83. chars++
  84. }
  85. }
  86. // Assume runes after snippets are one UTF-16 character each.
  87. return chars + runes
  88. }
  89. // RunesIndex converts the given index in utf16 characters to an index in runes.
  90. func (e *editorState) RunesIndex(chars int) int {
  91. if chars == -1 {
  92. return -1
  93. }
  94. if chars < e.Snippet.Start {
  95. // Assume runes before offset are one UTF-16 character each.
  96. return chars
  97. }
  98. runes := e.Snippet.Start
  99. chars -= e.Snippet.Start
  100. for _, r := range e.Snippet.Text {
  101. if chars == 0 {
  102. break
  103. }
  104. chars--
  105. runes++
  106. if r1, _ := utf16.EncodeRune(r); r1 != unicode.ReplacementChar {
  107. chars--
  108. }
  109. }
  110. // Assume runes after snippets are one UTF-16 character each.
  111. return runes + chars
  112. }