Skip to content

Commit e1cda7d

Browse files
Add predicates, properties, exports
1 parent 6fdd0c7 commit e1cda7d

File tree

1 file changed

+80
-7
lines changed

1 file changed

+80
-7
lines changed

src/DOM/Util/TextCursor.purs

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
module DOM.Util.TextCursor
22
( TextCursor(..)
3+
, content, length, null, empty, single
34
, _before, _selected, _after
5+
, atStart, atEnd, allSelected
6+
, isCursor, cursorAtStart, cursorAtEnd
7+
, isSelection, selectionAtStart, selectionAtEnd
48
, selectAll, moveCursorToStart, moveCursorToEnd
5-
, content, empty, single
69
, insert, mapAll
710
) where
811

912
import Prelude
13+
import Data.String (length, null) as S
1014
import Data.Symbol (SProxy(..))
1115
import Data.Newtype (class Newtype)
1216
import Data.Lens (Lens', (.~))
@@ -25,15 +29,56 @@ newtype TextCursor = TextCursor
2529

2630
derive instance textCursorNewtype :: Newtype TextCursor _
2731

32+
data ContentTest = Null | Any | Full
33+
type ContentPredicate =
34+
{ before :: ContentTest
35+
, selected :: ContentTest
36+
, after :: ContentTest
37+
}
38+
testContent :: ContentPredicate -> TextCursor -> Boolean
39+
testContent
40+
{ before, selected, after }
41+
(TextCursor { before: b, selected: s, after: a }) =
42+
test before b && test selected s && test after a
43+
where
44+
test Any _ = true
45+
test Null t = S.null t
46+
test Full t = not (S.null t)
47+
2848
-- | Get the current text in the field. (Everything before, inside, and after
2949
-- | the selection.)
3050
content :: TextCursor -> String
3151
content (TextCursor { before, selected, after }) = before <> selected <> after
3252

53+
-- | Get the length of the content of a `TextCursor`.
54+
-- check: length textcursor == length (content textcursor)
55+
length :: TextCursor -> Int
56+
length (TextCursor { before, selected, after }) =
57+
S.length before + S.length selected + S.length after
58+
3359
-- | An empty input field. No selection.
3460
empty :: TextCursor
3561
empty = TextCursor { before: "", selected: "", after: "" }
3662

63+
-- | Test whether the content of a `TextCursor` is empty.
64+
-- check: null textcursor == null (content textcursor)
65+
null :: TextCursor -> Boolean
66+
null = testContent { before: Null, selected: Null, after: Null }
67+
68+
-- | Test whether the selection is collapsed, i.e. acts like a cursor.
69+
isCursor :: TextCursor -> Boolean
70+
isCursor = testContent { before: Any, selected: Null, after: Any }
71+
72+
-- | Test whether some text is selected.
73+
isSelection :: TextCursor -> Boolean
74+
isSelection = testContent { before: Any, selected: Full, after: Any }
75+
76+
-- | Apply a `Lens` setting a value to an empty `TextCursor`. When used with
77+
-- | `_before`, `_selected`, or `_after` this will provide a `TextCursor` with
78+
-- | only one non-empty field.
79+
single :: Lens' TextCursor String -> String -> TextCursor
80+
single l v = l .~ v $ empty
81+
3782
-- | Lens for the text before the selection. Empty if the cursor is at the
3883
-- | beginning or the selection starts from the beginning.
3984
_before :: Lens' TextCursor String
@@ -48,12 +93,6 @@ _selected = _Newtype <<< prop (SProxy :: SProxy "selected")
4893
_after :: Lens' TextCursor String
4994
_after = _Newtype <<< prop (SProxy :: SProxy "after")
5095

51-
-- | Apply a `Lens` setting a value to an empty `TextCursor`. When used with
52-
-- | `beforeL`, `selectedL`, or `afterL` this will provide a `TextCursor` with
53-
-- | only one non-empty field.
54-
single :: Lens' TextCursor String -> String -> TextCursor
55-
single l v = l .~ v $ empty
56-
5796
-- | Map all three fields of the `TextCursor` with an endomorphism, performing
5897
-- | a replacement or other transformation such as normalization.
5998
mapAll :: (String -> String) -> TextCursor -> TextCursor
@@ -65,6 +104,7 @@ mapAll f (TextCursor { before, selected, after }) = TextCursor
65104

66105
-- | Move the cursor to the start of a field, preserving the overall text
67106
-- | content.
107+
-- Content-preserving idempotent endomorphism
68108
moveCursorToStart :: TextCursor -> TextCursor
69109
moveCursorToStart tc = TextCursor
70110
{ before: ""
@@ -75,6 +115,7 @@ moveCursorToStart tc = TextCursor
75115
-- | Select all of the text in a field.
76116
-- |
77117
-- | Note: selection direction is not specified.
118+
-- Content-preserving idempotent endomorphism
78119
selectAll :: TextCursor -> TextCursor
79120
selectAll tc = TextCursor
80121
{ before: ""
@@ -83,15 +124,47 @@ selectAll tc = TextCursor
83124
}
84125

85126
-- | Move the cursor to the end of a field, preserving the overall text content.
127+
-- Content-preserving idempotent endomorphism
86128
moveCursorToEnd :: TextCursor -> TextCursor
87129
moveCursorToEnd tc = TextCursor
88130
{ before: content tc
89131
, selected: ""
90132
, after: ""
91133
}
92134

135+
-- | Test whether there is a selection that ranges to the start.
136+
selectionAtStart :: TextCursor -> Boolean
137+
selectionAtStart = isSelection && atStart
138+
139+
-- | Test whether the cursor is at the start with no selection.
140+
cursorAtStart :: TextCursor -> Boolean
141+
cursorAtStart = isCursor && atStart
142+
143+
-- | Test whether the cursor or selection touches the start.
144+
atStart :: TextCursor -> Boolean
145+
atStart = testContent { before: Null, selected: Null, after: Any }
146+
147+
-- | Test whether the cursor has selected the whole field.
148+
allSelected :: TextCursor -> Boolean
149+
allSelected = testContent { before: Null, selected: Any, after: Null }
150+
151+
-- | Test whether the cursor or selection reaches the end.
152+
atEnd :: TextCursor -> Boolean
153+
atEnd = testContent { before: Any, selected: Any, after: Null }
154+
155+
-- | Test whether the cursor is at the end with no selection.
156+
cursorAtEnd :: TextCursor -> Boolean
157+
cursorAtEnd = isCursor && atEnd
158+
159+
-- | Test whether there is a selection that reaches the end.
160+
selectionAtEnd :: TextCursor -> Boolean
161+
selectionAtEnd = isSelection && atEnd
162+
93163
-- | Insert a string at the cursor position. If text is selected, the insertion
94164
-- | will be part of the selection. Otherwise it is inserted before the cursor.
165+
-- check:
166+
-- length (insert insertion textcursor)
167+
-- == length insertion + length textcursor
95168
insert :: String -> TextCursor -> TextCursor
96169
insert insertion = case _ of
97170
TextCursor { before, selected: "", after } -> TextCursor

0 commit comments

Comments
 (0)