Watched: 14 fns/opr within #.CommTools.CommTools
The test suite was executed once:
Executed at | APLVersion | Memory (MB) |
---|---|---|
2024-08-04 10:34:35 | Windows-64 ⋄ 19.0.49376.0 ⋄ W ⋄ Development ⋄ Unicode | 113 |
Overall 90% of the testable code is covered.
(Comment lines, empty lines, all :End
* lines etc. are ignored)
7 of the fns/opr are 100% covered.
Function/Operator | Lines not executed | Coverage | ≢ |
---|---|---|---|
#.CommTools.CommTools.Copyright | 1 | 0 | 1 |
#.CommTools.CommTools.Help | 1 | 0 | 1 |
#.CommTools.CommTools.Pause | 4←≢21,31-33 | 83 | 24 |
#.CommTools.CommTools.AskForNumber | 7←≢10-11,53-55,59,64 | 85 | 46 |
#.CommTools.CommTools.AskForText | 4←≢10-11,39,52 | 89 | 38 |
#.CommTools.CommTools.Select | 6←≢14-15,56-57,59-60 | 93 | 80 |
#.CommTools.CommTools.YesOrNo | 5←≢12-13,26,67,70 | 93 | 70 |
#.CommTools.CommTools.Assert | 100 | 0 | |
#.CommTools.CommTools.Cleanup | 100 | 5 | |
#.CommTools.CommTools.ErrNo | 100 | 1 | |
#.CommTools.CommTools.LF | 100 | 1 | |
#.CommTools.CommTools.Public | 100 | 8 | |
#.CommTools.CommTools.ReplaceCRbyLF | 100 | 0 | |
#.CommTools.CommTools.Version | 100 | 1 |
#.CommTools.CommTools.Copyright — 0%↟↑
r←Copyright
→[1] r←'Copyright by Kai Jaeger ⋄ https://kai-jaeger.de ⋄ kai@aplteam.com'
#.CommTools.CommTools.Help — 0%↟↑
Help
→[1] ⎕SE.UCMD'ADOC ',⍕⎕THIS
#.CommTools.CommTools.Pause — 83%↟↑
{flag}←{lineFlag}Pause msg
[1] ⍝ Prints `msg` to he session and tells the user that she must press <enter> if she wants to continue.
[2] ⍝ By entering "`∘∘∘`" the user may interrupt `Pause`: this activates a stop.\\
[3] ⍝ The optional left argument `lineFlag` defaults to 0. If it's 1 then a line is printed. The length
[4] ⍝ is defined by `⎕PW-1`.\\
[5] ⍝ You can prevent `Pause` from displaying the message and requiring the user to press <enter>,
[6] ⍝ refer to the documentation with ]ADoc CommTools\\
[7] ⍝ The function will return 1 in case it did present the message and stopped, and 0 otherwise.
[8] lineFlag←{0=⎕NC ⍵:0 ⋄ ⍎⍵}'lineFlag'
[9] msg←⊃LF{⍺,⍺⍺,⍵}/⊆msg
[10] msg←ReplaceCRbyLF msg
[11] flag←1
[12] :If AliasChar∊msg
[13] :If 0<⎕NC'NoPause'
[14] (alias msg)←{l←⍵⍳AliasChar ⋄ (l↑⍵)(l↓⍵)}msg
[15] flag←~∨/({⍵↑⍨⍵⍳AliasChar}¨⊆NoPause)≡¨⊂alias
[16] :EndIf
[17] :Else
[18] :If 0<⎕NC'NoPause'
[19] :If NoPause≡1
[20] :OrIf ∨/(⊆NoPause)∊⊆msg
→[21] :OrIf ∨/(NoPause/⍨~AliasChar∊¨NoPause){⍺≡¨(≢¨⍺)↑¨(≢⍺)⍴⊂⍵}{⍵↑⍨¯1+⍵⍳LF}msg
[22] flag←0
[23] :EndIf
[24] :EndIf
[25] :EndIf
[26] :If flag
[27] :If lineFlag
[28] ⍞←((⎕PW-1)⍴'─'),LF
[29] :EndIf
[30] :If AliasChar∊msg
→[31] ind←msg⍳AliasChar
→[32] :AndIf ∧/~(ind↑msg)∊' ',LF
→[33] msg←ind↓msg
[34] :EndIf
[35] input←⍞,0/⍞←({0=≢⍵:⍵ ⋄ ⍵,LF}msg),'In order to continue press <enter> '
[36] :If '∘∘∘'≡¯3↑input
[37] (1+⊃⎕LC)⎕STOP⊃⎕SI
[38] ∘∘∘ ⍝ Deliberate stop caused by user input
[39] :EndIf
[40] :EndIf
[41] ⍝Done
#.CommTools.CommTools.AskForNumber — 85%↟↑
value←{enforce}(CheckFn AskForNumber)question
[1] ⍝ Operator asking a question and allowing the user to enter a number.\\
[2] ⍝ By entering "`∘∘∘`" the user may interrupt `AskForNumber`: this activates a stop.\\
[3] ⍝ `CheckFn` is supposed to be a function that gets the input as `⍵` and must return a Boolean
[4] ⍝ with a 1 indicating that the input is okay and 0 that it is not. If you don't need/want
[5] ⍝ a check function pass `{1}` as left operand.\\
[6] ⍝ If the user refuses to enter a number an empty vector is returned.
[7] ⍝ However, you might prevent this from happening by specifying a 1 as `⍺` (`enforce`), meaning that
[8] ⍝ the user must enter a number in order to continue.
[9] :If '?'≡question
→[10] value←(⍕⎕THIS),'.AskForNumber_Answers←1 2⍴'' '' '' '''
→[11] :Return
[12] :EndIf
[13] enforce←{0<⎕NC ⍵:⍎⍵ ⋄ 0}'enforce'
[14] value←⍬
[15] success←flag←0
[16] :If ~(≡question)∊0 1
[17] question←1↓⊃,/LF,¨question
[18] :EndIf
[19] question←ReplaceCRbyLF question
[20] question←{⍵↓⍨+/∧\' '=⍵}question
[21] :Repeat
[22] firstPart←{⍵↑⍨⍵⍳LF}question
[23] :If 0<⎕NC'AskForNumber_Answers'
[24] AskForNumber_Answers←(0<≢¨' '~⍨¨AskForNumber_Answers[;1])⌿AskForNumber_Answers
[25] AskForNumber_Answers[;1]←{⍵↓⍨+/∧\' '=⍵}¨AskForNumber_Answers[;1]
[26] :AndIf 0<≢AskForNumber_Answers
[27] :If AliasChar∊firstPart
[28] ind←firstPart⍳AliasChar
[29] :AndIf ∧/~(LF,' ')∊ind↑question
[30] (alias question)←ind{(⍺↑⍵)(⍺↓⍵)}firstPart
[31] flag←0<+/bool←({⍵↑⍨⍵⍳AliasChar}¨AskForNumber_Answers[;1])≡¨⊂alias
[32] :ElseIf ~flag←0<+/bool←AskForNumber_Answers[;1]≡¨⊂firstPart
[33] flag←0<+/bool←firstPart∘{⍵≡(≢⍵)↑⍺}¨AskForNumber_Answers[;1]
[34] :EndIf
[35] :If flag
[36] 'Multiple pre-prepared answers qualify?!'Assert 1=+/bool
[37] input←⍕2⊃AskForNumber_Answers[bool⍳1;]
[38] :EndIf
[39] :Else
[40] :If (⌊/firstPart⍳' ',LF)>firstPart⍳AliasChar
[41] question←{~AliasChar∊⍵:⍵ ⋄ ⍵↓⍨⍵⍳AliasChar}question
[42] :EndIf
[43] :EndIf
[44] :If ~flag
[45] input←⍞,0/⍞←question,': '
[46] :If '∘∘∘'≡¯3↑input
[47] (1+⊃⎕LC)⎕STOP⊃⎕SI
[48] ∘∘∘ ⍝ Deliberate stop caused by user input
[49] :EndIf
[50] input←{⍵↑⍨1+-(⌽⍵)⍳':'}input
[51] :EndIf
[52] :If 0=≢input~' '
→[53] :AndIf ~enforce
→[54] value←⍬
→[55] :Return
[56] :Else
[57] (valid value)←⎕VFI input
[58] :If 1≠≢valid
→[59] ⎕←'Please enter precisely one number'
[60] :ElseIf valid
[61] success←CheckFn value
[62] value←⊃value
[63] :Else
→[64] ⎕←'You did not enter a valid number, please retry!'
[65] :EndIf
[66] :EndIf
[67] :Until success
[68] ⍝Done
#.CommTools.CommTools.AskForText — 89%↟↑
text←{enforce}(CheckFn AskForText)question
[1] ⍝ Operator asking a question and allowing the user to enter text.\\
[2] ⍝ By entering "`∘∘∘`" the user may interrupt `AskForText`: this activates a stop.\\
[3] ⍝ `CheckFn` is supposed to be a function that gets the input as `⍵` and must return a Boolean
[4] ⍝ with a 1 indicating that the input is okay and 0 that it is not. If you don't need/want
[5] ⍝ a check function pass `{1}` as left operand.\\
[6] ⍝ If the user refuses to enter anything an empty vector is returned.
[7] ⍝ However, you might prevent this from happening by specifying a 1 as `⍺` (`enforce`), meaning that
[8] ⍝ the user must enter something in order to continue.
[9] :If '?'≡question
→[10] text←(⍕⎕THIS),'.AskForText_Answers←1 2⍴'' '' '' '''
→[11] :Return
[12] :EndIf
[13] enforce←{0<⎕NC ⍵:⍎⍵ ⋄ 0}'enforce'
[14] text←''
[15] success←flag←0
[16] :If ~(≡question)∊0 1
[17] question←1↓⊃,/LF,¨question
[18] :EndIf
[19] question←ReplaceCRbyLF question
[20] question←{⍵↓⍨+/∧\' '=⍵}question
[21] :Repeat
[22] firstPart←{⍵↑⍨⍵⍳LF}question
[23] :If 0<⎕NC'AskForText_Answers'
[24] AskForText_Answers←(0<≢¨' '~⍨¨AskForText_Answers[;1])⌿AskForText_Answers
[25] AskForText_Answers[;1]←{⍵↓⍨+/∧\' '=⍵}¨AskForText_Answers[;1]
[26] :AndIf 0<≢AskForText_Answers
[27] :If AliasChar∊question
[28] (alias question)←{l←⍵⍳AliasChar ⋄ (l↑⍵)(l↓⍵)}question
[29] flag←0<+/bool←({⍵↑⍨⍵⍳AliasChar}¨AskForText_Answers[;1])≡¨⊂alias
[30] :ElseIf ~flag←0<+/bool←AskForText_Answers[;1]≡¨⊂question
[31] flag←0<+/bool←question∘{⍵≡(≢⍵)↑⍺}¨AskForText_Answers[;1]
[32] :EndIf
[33] :If flag
[34] 'Multiple pre-prepared answers qualify?!'Assert 1=+/bool
[35] text←2⊃AskForText_Answers[bool⍳1;]
[36] :EndIf
[37] :Else
[38] :If (⌊/firstPart⍳' ',LF)>firstPart⍳AliasChar
→[39] question←{~AliasChar∊⍵:⍵ ⋄ ⍵↓⍨⍵⍳AliasChar}question
[40] :EndIf
[41] :EndIf
[42] :If ~flag
[43] text←⍞,0/⍞←question,': '
[44] :If '∘∘∘'≡¯3↑text
[45] (1+⊃⎕LC)⎕STOP⊃⎕SI
[46] ∘∘∘ ⍝ Deliberate stop caused by user input
[47] :EndIf
[48] text←(≢{⍵↑⍨-¯1+(⌽⍵)⍳LF}question,': ')↓text
[49] :EndIf
[50] :If 0=≢text
[51] :If enforce
→[52] ⎕←'You must enter something!'
[53] :Else
[54] :Return
[55] :EndIf
[56] :Else
[57] success←CheckFn text
[58] :EndIf
[59] :Until success
[60] ⍝Done
#.CommTools.CommTools.Select — 93%↟↑
index←{x}Select choices
[1] ⍝ Presents `choices` as a numbered list and allows the user to select either exactly one or multiple ones.
[2] ⍝ Selecting just one is the default.\\
[3] ⍝ The optional left argument allows you to specify multiple (positional) choices:
[4] ⍝ * `caption` is shown above the choices; must be a simple character vector
[5] ⍝ * `manyFlag` defaults to 0 (meaning just one item might be selected) or 1, in which case multiple items can be selected
[6] ⍝ * `mustFlag` forces the user to select at least one option\\
[7] ⍝ Notes:
[8] ⍝ * `choices` must not have more than 999 items\\
[9] ⍝ * By entering "`∘∘∘`" the user may interrupt `Select` by running onto a stop vector, overcoming the bug that ⍞ cannot be interrupted
[10] ⍝ * If the user aborts by entering "q" (for "quit") `⍬` will be returned
[11] ⍝ You can make `Select` select none, one or several choices automatically, refer to the documentation
[12] ⍝ with ]ADoc CommTools\\
[13] :If '?'≡choices
→[14] index←(⍕⎕THIS),'.Select_Choices←1 2⍴'' '' '' '''
→[15] :Return
[16] :EndIf
[17] x←{0<⎕NC ⍵:⊆⍎⍵ ⋄ ''}'x'
[18] (caption manyFlag mustFlag)←x,(⍴,x)↓'' 0 0
[19] '"caption" must be a simple character vector'Assert 1=≡caption←,caption
[20] '"caption" must not contain line feeds (⎕UCS 10)'Assert~LF∊caption
[21] '"caption" must not contain carriage returns (⎕UCS 13)'Assert~(⎕UCS 13)∊caption
[22] ⎕IO←1 ⋄ ⎕ML←1
[23] 'Invalid right argument; must be a vector of text vectors.'⎕SIGNAL ErrNo/⍨2≠|≡choices
[24] 'Right argument has more than 999 items'⎕SIGNAL ErrNo/⍨999<≢choices
[25] flag←0
[26] firstPart←{⍵↑⍨⍵⍳LF}caption
[27] :If 0<⎕NC'Select_Choices'
[28] Select_Choices←(0<≢¨Select_Choices[;1])⌿Select_Choices
[29] :AndIf 0<≢Select_Choices
[30] :If AliasChar∊caption
[31] (alias caption)←{l←⍵⍳AliasChar ⋄ (l↑⍵)(l↓⍵)}caption
[32] flag←0<+/bool←({⍵↑⍨(,⍵)⍳AliasChar}¨Select_Choices[;1])≡¨⊂alias
[33] :Else
[34] :If 0=+/bool←Select_Choices[;1]≡¨⊂caption
[35] bool←caption∘{⍵≡⍺↑⍨≢⍵}¨Select_Choices[;1]
[36] :EndIf
[37] flag←0<+/bool
[38] :EndIf
[39] :If flag
[40] 'Multiple choices qualify?!'Assert 1=+/bool
[41] index←2⊃Select_Choices[bool⍳1;]
[42] :If (⊂index)∊0 ⍬
[43] index←⍬
[44] :ElseIf ' '=1↑0⍴∊index ⍝ Text?!
[45] :If '*'=¯1↑index
[46] index←((¯1+≢index)↑¨{⍵↓⍨+/∧\' '=⍵}¨choices)⍳⊂(¯1↓index)
[47] 'Invalid selection'Assert index∊⍳≢choices
[48] :ElseIf '*'=1↑index
[49] index←((-¯1+≢index)↑¨{⍵↓⍨+/∧\' '=⍵}¨choices)⍳⊂(1↓index)
[50] 'Invalid selection'Assert index∊⍳≢choices
[51] :ElseIf (,'a')≡,index
[52] index←⍳≢choices
[53] :ElseIf (,'q')≡,index
[54] index←⍬
[55] :ElseIf 0<≢buff←⍸index∘≡¨choices
→[56] index←buff
→[57] 'Invalid selection'Assert index∊⍳≢choices
[58] :ElseIf 1=≢index←⍸index∘≡¨(≢index)↑¨{⍵↓⍨+/∧\' '=⍵}¨choices
→[59] index←⊃index
→[60] 'Invalid selection'Assert index∊⍳≢choices
[61] :Else
[62] 'Invalid selection'Assert 0
[63] :EndIf
[64] :Else
[65] 'Invalid selection'Assert∧/index∊⍳≢choices
[66] :EndIf
[67] :EndIf
[68] :EndIf
[69] :If ~flag
[70] flag2←0
[71] :Repeat
[72] blankFlag←0≠≢caption
[73] :If AliasChar∊caption
[74] :If (firstPart⍳' ')>firstPart⍳AliasChar
[75] caption←{~AliasChar∊⍵:⍵ ⋄ ⍵↓⍨⍵⍳AliasChar}caption
[76] :EndIf
[77] :EndIf
[78] ⎕←{⍵↑'---',(blankFlag/' '),caption,(blankFlag/' '),⍵⍴'-'}⎕PW-1
[79] ⎕←⍪{((⊂'. '),¨⍨(⊂3 0)⍕¨⍳≢⍵),¨⍵}choices
[80] ⎕←''
[81] question←'Select one ',(manyFlag/'or more '),'item',((manyFlag)/'s'),' '
[82] question,←((manyFlag∨~mustFlag)/'('),((~mustFlag)/'q=quit'),((manyFlag∧~mustFlag)/', '),(manyFlag/'a=all'),((manyFlag∨~mustFlag)/')'),' :'
[83] :If 0<≢answer←⍞,0/⍞←question
[84] answer←(⍴question)↓answer
[85] :If '∘∘∘'≡¯3↑answer
[86] (1+⊃⎕LC)⎕STOP⊃⎕SI
[87] ∘∘∘ ⍝ Deliberate stop caused by user input
[88] :EndIf
[89] :If 1=≢answer
[90] :AndIf answer∊'Qq',manyFlag/'Aa'
[91] :If answer∊'Qq'
[92] :If 0=mustFlag
[93] index←⍬
[94] flag2←1
[95] :EndIf
[96] :Else
[97] index←⍳≢choices
[98] flag2←1
[99] :EndIf
[100] :Else
[101] (bool value)←⎕VFI answer
[102] :If ∧/bool
[103] :AndIf manyFlag∨1=+/bool
[104] value←bool/value
[105] :AndIf ∧/value∊⍳⍴choices
[106] index←value
[107] flag2←0≠≢index
[108] :EndIf
[109] :EndIf
[110] :EndIf
[111] :Until flag2
[112] index←{1<≢⍵:⍵ ⋄ ⊃⍵}⍣(⍬≢index)⊣index
[113] :EndIf
[114] ⍝Done
#.CommTools.CommTools.YesOrNo — 93%↟↑
yesOrNo←{default}YesOrNo question
[1] ⍝ Asks a simple question and allows just "Y" (or "y") or "N" (or "n") as answers.\\
[2] ⍝ The question may be a simple character vector, possibly with `⎕UCS 10` in between,
[3] ⍝ or a vector of simple character vectors.\\
[4] ⍝ You may specify a default via the optional left argument which when specified
[5] ⍝ rules what happens when the user just presses <enter>.
[6] ⍝ `default` must be either 1 (yes) or 0 (no).\\
[7] ⍝ By entering "`∘∘∘`" the user may interrupt `YesOrNo`: this activates a stop.\\
[8] ⍝ You can make `YesOrNo` answer the question automatically, refer to the documentation
[9] ⍝ with ]ADoc CommTools\\
[10] ⍝ Note that this function does **not** work as intended when traced!
[11] :If '?'≡question
→[12] yesOrNo←(⍕⎕THIS),'.YesOrNo_Answers←0 2⍴'' '''
→[13] :Return
[14] :EndIf
[15] isOkay←0
[16] default←{0<⎕NC ⍵:⍎⍵ ⋄ ''}'default'
[17] isOkay←0
[18] :If ~(≡question)∊0 1
[19] question←1↓⊃,/LF,¨question
[20] :EndIf
[21] question←ReplaceCRbyLF question
[22] question←{⍵↓⍨+/∧\' '=⍵}question
[23] :If 0≠≢default
[24] 'Left argument must be a scalar'⎕SIGNAL 11/⍨1≠≢default
[25] :AndIf ~default∊0 1
→[26] 'The left argument. if specified, must be a Boolean or empty'⎕SIGNAL 11
[27] :EndIf
[28] flag←1
[29] firstPart←{⍵↑⍨⍵⍳LF}question
[30] :If 0<⎕NC'YesOrNo_Answers'
[31] YesOrNo_Answers←(0<≢¨' '~⍨¨YesOrNo_Answers[;1])⌿YesOrNo_Answers
[32] :AndIf 0<≢YesOrNo_Answers
[33] :If AliasChar∊firstPart
[34] ind←firstPart⍳AliasChar
[35] (alias question)←ind{(⍺↑⍵)(⍺↓⍵)}firstPart
[36] flag←0<+/bool←({⍵↑⍨⍵⍳AliasChar}¨YesOrNo_Answers[;1])≡¨⊂alias
[37] :ElseIf ~flag←0<+/bool←YesOrNo_Answers[;1]≡¨⊂question
[38] :AndIf ~flag←0<+/bool←question∘{⍵≡(≢⍵)↑⍺}¨YesOrNo_Answers[;1]
[39] :AndIf LF∊question
[40] buff←{⍵↓⍨+/∧\' '=⍵}⊃¯1↑LF(≠⊆⊢)question
[41] flag←0<+/bool←YesOrNo_Answers[;1]≡¨⊂buff
[42] :EndIf
[43] :If flag
[44] 'Multiple pre-prepared answers qualify?!'Assert 1=+/bool
[45] answer←2⊃YesOrNo_Answers[bool⍳1;]
[46] :If 0=≢answer
[47] yesOrNo←default
[48] :Else
[49] ('Invalid answer: ',answer)Assert answer∊'YyNn'
[50] yesOrNo←answer∊'Yy'
[51] :EndIf
[52] :EndIf
[53] :Else
[54] flag←0
[55] :EndIf
[56] :If ~flag
[57] :If 0=≢default
[58] add←' (y/n) '
[59] :Else
[60] :If default
[61] add←' (Y/n) '
[62] :Else
[63] add←' (y/N) '
[64] :EndIf
[65] :EndIf
[66] :If 1<≡question
→[67] question←1↓⊃,/LF,¨question
[68] :EndIf
[69] :If (⌊/firstPart⍳' ',LF)>firstPart⍳AliasChar
→[70] question←{~AliasChar∊⍵:⍵ ⋄ ⍵↓⍨⍵⍳AliasChar}question,add
[71] :Else
[72] question,←add
[73] :EndIf
[74] :Repeat
[75] ⎕←''
[76] ⍞←question
[77] answer←⍞
[78] :If answer≡question ⍝ Did ... (since version 18.0 trailing blanks are not removed anymore)
[79] :OrIf (≢answer)=¯1+≢question ⍝ ... the ...
[80] :OrIf 0=≢answer ⍝ ... user ...
[81] :OrIf question≡(-≢question)↑answer ⍝ ... just ...
[82] dtb←{⍵↓⍨-+/∧\' '=⌽⍵}
[83] answer2←dtb answer
[84] :OrIf answer2≡((-≢answer2)↑LF{~⍺∊⍵:⍵ ⋄ ' ',dtb ⍺{⌽⍵↑⍨1+⍵⍳⍺}⌽⍵}question) ⍝ ... press ...
[85] :OrIf answer≡{1↓⊃¯1↑(⍵=LF)⊂⍵}LF,question ⍝ ... <enter>?
[86] :If 0≠≢default
[87] yesOrNo←default
[88] isOkay←1
[89] :EndIf
[90] :Else
[91] :If '∘∘∘'≡¯3↑answer
[92] (1+⊃⎕LC)⎕STOP⊃⎕SI
[93] ∘∘∘ ⍝ Deliberate stop caused by user input
[94] :EndIf
[95] answer←¯1↑{⍵↓⍨-+/∧\' '=⌽⍵}answer
[96] :If answer∊'YyNn'
[97] isOkay←1
[98] yesOrNo←answer∊'Yy'
[99] :EndIf
[100] :EndIf
[101] :Until isOkay
[102] :EndIf
[103] ⍝Done
#.CommTools.CommTools.Assert — 100%↟↑
Assert←{⍺←'' ⋄ (,1)≡,⍵:r←1 ⋄ ⎕ML←1 ⋄ ⍺ ⎕SIGNAL 1↓(⊃∊⍵),11}
#.CommTools.CommTools.Cleanup — 100%↟↑
Cleanup
[1] ⎕EX'Select_Choices'
[2] ⎕EX'YesOrNo_Answers'
[3] ⎕EX'NoPause'
[4] ⎕EX'AskForNumber_Answers'
[5] ⎕EX'AskForText_Answers'
#.CommTools.CommTools.ErrNo — 100%↟↑
r←ErrNo
[1] r←811
#.CommTools.CommTools.LF — 100%↟↑
r←LF
[1] r←⎕UCS 10
#.CommTools.CommTools.Public — 100%↟↑
r←Public
[1] r←''
[2] r,←⊂'AskForText'
[3] r,←⊂'AskForNumber'
[4] r,←⊂'Select'
[5] r,←⊂'YesOrNo'
[6] r,←⊂'Pause'
[7] r,←⊂'Cleanup'
[8] r,←⊂'Help'
#.CommTools.CommTools.ReplaceCRbyLF — 100%↟↑
ReplaceCRbyLF←{LF@(⍸⍵=⎕UCS 13)⊣⍵}
#.CommTools.CommTools.Version — 100%↟↑
r←Version
[1] ⍝ See also `History`
[2] r←'CommTools' '1.7.1' '2023-11-04'