Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
This repository was archived by the owner on Apr 5, 2025. It is now read-only.

Commit 09b4f46

Browse filesBrowse files
Fix completion of command when there is more text after
1 parent 0a6786e commit 09b4f46
Copy full SHA for 09b4f46

File tree

Expand file treeCollapse file tree

7 files changed

+104
-82
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+104
-82
lines changed

‎src/Fargo/Fargo.fs

Copy file name to clipboardExpand all lines: src/Fargo/Fargo.fs
+10-6Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ module Fargo =
127127
if Usage.isStop pos x then
128128
Usage.complete usage x.Text
129129
elif Usage.isMatch usage x then
130-
if pos <= y.Extent.End then
130+
if pos > x.Extent.End && pos <= y.Extent.End then
131131
(complete y.Text tokens, true)
132132
else
133133
[], false
@@ -376,11 +376,15 @@ module Fargo =
376376
let complete pos tokens =
377377
match px tokens with
378378
| Ok x, restx, usagex ->
379-
let pf, cf = f x
380-
cf pos restx
379+
if not (Tokens.contains pos tokens) || Tokens.contains pos restx then
380+
let _pf, cf = f x
381+
cf pos restx
382+
else
383+
cx pos tokens
381384
| Error _, _, _ ->
382385
cx pos tokens
383386
parse, complete
387+
384388
let ret (x: 'a) : Arg<'a> =
385389
let parse tokens =
386390
Ok x, tokens, Usages.empty
@@ -804,7 +808,7 @@ Register-ArgumentCompleter -Native -CommandName %s -ScriptBlock {
804808
}
805809

806810

807-
let run appName ((p,c): Arg<'a>) (cmdLine: string[]) (f: CancellationToken ->'a -> Task<int>) : int =
811+
let run appName ((p,c): Arg<'a>) (cmdLine: string[]) (f: CancellationToken -> 'a -> Task<int>) : int =
808812
use cts = new CancellationTokenSource()
809813
let mutable graceful = true
810814
Console.CancelKeyPress
@@ -817,15 +821,15 @@ Register-ArgumentCompleter -Native -CommandName %s -ScriptBlock {
817821
else
818822
printfn $"{Colors.red}[Ctrl+C]Force stop{Colors.def}"
819823
)
820-
let tokens = Token.ofCmdLine cmdLine
824+
let tokens = Tokens.ofCmdLine cmdLine
821825
let runner =
822826
innerRun (pRun appName) tokens (fun cmd ->
823827
task {
824828
match cmd with
825829
| TopComplete(pos, rest) ->
826830
let cmdTokens =
827831
match rest with
828-
| [x] -> Token.ofString x.Text
832+
| [x] -> Tokens.ofString x.Text
829833
| _ -> rest
830834
for result in complete (fargo { do! pRemoveAppName appName
831835
return! (p,c)}) pos cmdTokens do

‎src/Fargo/Token.fs

Copy file name to clipboardExpand all lines: src/Fargo/Token.fs
+43-38Lines changed: 43 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ module Extent =
3737
pos >= extent.Start && pos <= extent.End
3838

3939
module Token =
40-
open Microsoft.FSharp.Core.CompilerServices
4140

4241
let inline extent s e = { Start = s; End = e}
4342

@@ -54,41 +53,44 @@ module Token =
5453
| Quotes q -> Some q
5554
| StartQuote _ | NoQuotes -> None
5655

56+
57+
module Tokens =
58+
open Microsoft.FSharp.Core.CompilerServices
5759
let rec private loop (input: string) (pos: int) (result: ListCollector<_> byref) =
58-
if pos >= input.Length then
59-
result.Close()
60+
if pos >= input.Length then
61+
result.Close()
62+
else
63+
if input[pos] = '"' then
64+
loopQuote '"' input pos &result
65+
elif input[pos] = '\'' then
66+
loopQuote '\'' input pos &result
6067
else
61-
if input[pos] = '"' then
62-
loopQuote '"' input pos &result
63-
elif input[pos] = '\'' then
64-
loopQuote '\'' input pos &result
65-
else
66-
match input.IndexOf(' ', pos) with
67-
| -1 ->
68-
result.Add { Text = input.Substring(pos)
69-
Extent = extent pos input.Length
70-
Quotes = NoQuotes}
71-
result.Close()
72-
| n ->
73-
let txt = input.Substring(pos, n-pos)
74-
if txt <> "" then
75-
result.Add { Text = txt
76-
Extent = extent pos n
77-
Quotes = NoQuotes }
78-
loop input (n+1) &result
68+
match input.IndexOf(' ', pos) with
69+
| -1 ->
70+
result.Add { Text = input.Substring(pos)
71+
Extent = Token.extent pos input.Length
72+
Quotes = NoQuotes}
73+
result.Close()
74+
| n ->
75+
let txt = input.Substring(pos, n-pos)
76+
if txt <> "" then
77+
result.Add { Text = txt
78+
Extent = Token.extent pos n
79+
Quotes = NoQuotes }
80+
loop input (n+1) &result
7981
and loopQuote quote (input: string) (pos: int) (result: ListCollector<_> byref) =
80-
match input.IndexOf(quote, pos+1) with
81-
| -1 ->
82-
result.Add { Text = input.Substring(pos+1)
83-
Extent = extent (pos+1) input.Length
84-
Quotes = StartQuote quote}
85-
result.Close()
86-
| n ->
87-
let txt = input.Substring(pos+1, n-pos-1)
88-
result.Add { Text = txt
89-
Extent = extent (pos+1) n
90-
Quotes = Quotes quote }
91-
loop input (n+1) &result
82+
match input.IndexOf(quote, pos+1) with
83+
| -1 ->
84+
result.Add { Text = input.Substring(pos+1)
85+
Extent = Token.extent (pos+1) input.Length
86+
Quotes = StartQuote quote}
87+
result.Close()
88+
| n ->
89+
let txt = input.Substring(pos+1, n-pos-1)
90+
result.Add { Text = txt
91+
Extent = Token.extent (pos+1) n
92+
Quotes = Quotes quote }
93+
loop input (n+1) &result
9294

9395
let ofString (input: string) =
9496
if isNull input then
@@ -109,7 +111,7 @@ module Token =
109111
Quotes '\''
110112
else
111113
NoQuotes
112-
result.Add({Text = token; Extent = extent pos (pos + token.Length); Quotes = quotes} )
114+
result.Add({Text = token; Extent = Token.extent pos (pos + token.Length); Quotes = quotes} )
113115
pos <- pos + token.Length + 1
114116

115117
result.Close()
@@ -124,20 +126,23 @@ module Token =
124126
let rec loop pos tokens =
125127
match tokens with
126128
| token :: rest ->
127-
let extent = outerExtent token
129+
let extent = Token.outerExtent token
128130
let startPad = max (extent.Start - pos) 0
129131
builder.Append(' ', startPad) |> ignore
130132

131-
startQuote token
133+
Token.startQuote token
132134
|> Option.iter (fun q -> builder.Append(q) |> ignore)
133135

134136
builder.Append(token.Text) |> ignore
135137

136-
endQuote token
138+
Token.endQuote token
137139
|> Option.iter (fun q -> builder.Append(q) |> ignore)
138140

139141

140142
loop extent.End rest
141143
| [] -> builder.ToString()
142144
loop 0 (tokens |> List.sortBy (fun t -> t.Extent))
143-
145+
146+
let contains pos (tokens: Token list) =
147+
tokens
148+
|> List.exists (Token.outerExtent >> (Extent.contains pos))

‎tests/Fargo.Test/AllAtOnce.fs

Copy file name to clipboardExpand all lines: tests/Fargo.Test/AllAtOnce.fs
+15-2Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ let p =
6565

6666

6767
let parse input =
68-
tryParseTokens p (Token.ofString input)
68+
tryParseTokens p (Tokens.ofString input)
6969
|> Result.mapError (fun (errs,usages) -> errs, [ for u in usages.Options -> u.Name |> Option.defaultValue "" ])
7070

7171
let complete pos input =
72-
Fargo.Run.complete p pos (Token.ofString input)
72+
Fargo.Run.complete p pos (Tokens.ofString input)
7373

7474
let complete2 pos input =
7575
Testing.withStdout (fun _ ->
@@ -217,3 +217,16 @@ let ``completion of command complete``() =
217217
=! """select"""
218218

219219

220+
[<Fact>]
221+
let ``completion of command on a token in the middle``() =
222+
complete2 10 "voice sele --voice funny"
223+
=! "select"
224+
225+
[<Fact>]
226+
let ``completion of command on a full token in the middle``() =
227+
complete2 12 "voice select --voice funny"
228+
=! "select"
229+
230+
231+
232+

‎tests/Fargo.Test/Completion.fs

Copy file name to clipboardExpand all lines: tests/Fargo.Test/Completion.fs
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ open FsCheck.Xunit
88
open DEdge.Diffract
99

1010
let complete (arg: Arg<_>) pos cmdLine =
11-
complete arg pos (Token.ofString cmdLine)
11+
complete arg pos (Tokens.ofString cmdLine)
1212

1313
let (=!) (actual:'a) (expected: 'a) = Differ.Assert(expected, actual )
1414

‎tests/Fargo.Test/Parsing.fs

Copy file name to clipboardExpand all lines: tests/Fargo.Test/Parsing.fs
+5-5Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ let (=!) (actual:'a) (expected: 'a) = Differ.Assert(expected, actual )
1010

1111

1212
let parse ((p,_): Arg<'t>) input =
13-
let result, _, _ = p (Token.ofString input)
13+
let result, _, _ = p (Tokens.ofString input)
1414
result
1515

1616
let rest ((p,c): Arg<'t>) input =
17-
let _, tokens, _ = p (Token.ofString input)
18-
Token.toString tokens
17+
let _, tokens, _ = p (Tokens.ofString input)
18+
Tokens.toString tokens
1919

2020
let usage ((p,c): Arg<'t>) input =
21-
let _, _, usages = p (Token.ofString input)
21+
let _, _, usages = p (Tokens.ofString input)
2222
usages.Options
2323

2424

@@ -904,6 +904,6 @@ module Error =
904904

905905
[<Property>]
906906
let ``Errorf always fails``(NonNull fargo) =
907-
let p = errorf (fun tokens -> Token.toString tokens )
907+
let p = errorf (fun tokens -> Tokens.toString tokens )
908908
parse p fargo
909909
=! Error [ fargo.TrimEnd(' ')]

‎tests/Fargo.Test/Token.fs

Copy file name to clipboardExpand all lines: tests/Fargo.Test/Token.fs
+29-29Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,98 +17,98 @@ let qtoken q text s e = { Text = text; Extent = { Start = s; End = e}; Quotes =
1717
let sqtoken q text s e = { Text = text; Extent = { Start = s; End = e}; Quotes = StartQuote q}
1818

1919
[<Fact>]
20-
let ``Token.ofList should produce correst Start and End``() =
21-
Token.ofList ["cmd";"--arg";"value"]
20+
let ``Tokens.ofList should produce correst Start and End``() =
21+
Tokens.ofList ["cmd";"--arg";"value"]
2222
=! [ token "cmd" 0 3
2323
token "--arg" 4 9
2424
token "value" 10 15]
2525

2626
[<Fact>]
27-
let ``Token.ofList should skip null entries``() =
28-
Token.ofList ["cmd";null;"value"]
27+
let ``Tokens.ofList should skip null entries``() =
28+
Tokens.ofList ["cmd";null;"value"]
2929
=! [ token "cmd" 0 3
3030
token "value" 4 9 ]
3131
[<Fact>]
32-
let ``Token.ofString should accept null``() =
33-
Token.ofString null
32+
let ``Tokens.ofString should accept null``() =
33+
Tokens.ofString null
3434
=! []
3535

3636
[<Fact>]
37-
let ``Token.ofString should split spaces``() =
38-
Token.ofString "cmd --arg value"
37+
let ``Tokens.ofString should split spaces``() =
38+
Tokens.ofString "cmd --arg value"
3939
=! [ token "cmd" 0 3
4040
token "--arg" 4 9
4141
token "value" 10 15 ]
4242

4343
[<Fact>]
44-
let ``Token.ofString should accept multiple spaces and produce Start and End accordingly``() =
45-
Token.ofString "cmd --arg value"
44+
let ``Tokens.ofString should accept multiple spaces and produce Start and End accordingly``() =
45+
Tokens.ofString "cmd --arg value"
4646
=! [ token "cmd" 0 3
4747
token "--arg" 6 11
4848
token "value" 13 18 ]
4949

5050
[<Fact>]
51-
let ``Token.ofString should treat double quotes as a single token and remove quotes``() =
52-
Token.ofString """cmd --arg "some value" --flag"""
51+
let ``Tokens.ofString should treat double quotes as a single token and remove quotes``() =
52+
Tokens.ofString """cmd --arg "some value" --flag"""
5353
=! [ token "cmd" 0 3
5454
token "--arg" 4 9
5555
qtoken '"' "some value" 11 21
5656
token "--flag" 23 29 ]
5757

5858
[<Fact>]
59-
let ``Token.ofString should not fail on missing end double quote``() =
60-
Token.ofString """cmd --arg "some value --flag"""
59+
let ``Tokens.ofString should not fail on missing end double quote``() =
60+
Tokens.ofString """cmd --arg "some value --flag"""
6161
=! [ token "cmd" 0 3
6262
token "--arg" 4 9
6363
sqtoken '"' "some value --flag" 11 28 ]
6464

6565
[<Fact>]
66-
let ``Token.ofString should treat single quotes as a single token and remove quotes``() =
67-
Token.ofString """cmd --arg 'some value' --flag"""
66+
let ``Tokens.ofString should treat single quotes as a single token and remove quotes``() =
67+
Tokens.ofString """cmd --arg 'some value' --flag"""
6868
=! [ token "cmd" 0 3
6969
token "--arg" 4 9
7070
qtoken '\'' "some value" 11 21
7171
token "--flag" 23 29]
7272

7373
[<Fact>]
74-
let ``Token.ofString should not fail on missing end single quote``() =
75-
Token.ofString """cmd --arg 'some value --flag"""
74+
let ``Tokens.ofString should not fail on missing end single quote``() =
75+
Tokens.ofString """cmd --arg 'some value --flag"""
7676
=! [ token "cmd" 0 3
7777
token "--arg" 4 9
7878
sqtoken '\'' "some value --flag" 11 28 ]
7979

8080
[<Fact>]
81-
let ``Token.ofString should accept single quotes in double quotes``() =
82-
Token.ofString """cmd --arg "some 'value" --flag"""
81+
let ``Tokens.ofString should accept single quotes in double quotes``() =
82+
Tokens.ofString """cmd --arg "some 'value" --flag"""
8383
=! [ token "cmd" 0 3
8484
token "--arg" 4 9
8585
qtoken '"' "some 'value" 11 22
8686
token "--flag" 24 30]
8787

8888
[<Fact>]
89-
let ``Token.ofString should accept double quotes in single quotes``() =
90-
Token.ofString """cmd --arg 'some "value' --flag"""
89+
let ``Tokens.ofString should accept double quotes in single quotes``() =
90+
Tokens.ofString """cmd --arg 'some "value' --flag"""
9191
=! [ token "cmd" 0 3
9292
token "--arg" 4 9
9393
qtoken '\'' """some "value""" 11 22
9494
token "--flag" 24 30 ]
9595

9696
[<Property>]
97-
let ``Token.ofCmdLine should behave as Token.ofString for single argument, Token.ofList otherwhise`` (args: string[]) =
97+
let ``Tokens.ofCmdLine should behave as Tokens.ofString for single argument, Tokens.ofList otherwhise`` (args: string[]) =
9898
match args with
99-
| [| x |] -> Token.ofCmdLine args = Token.ofString x
100-
| _ -> Token.ofCmdLine args = Token.ofList (Array.toList args)
99+
| [| x |] -> Tokens.ofCmdLine args = Tokens.ofString x
100+
| _ -> Tokens.ofCmdLine args = Tokens.ofList (Array.toList args)
101101

102102
[<Property>]
103-
let ``Token.ofString then Token.toString should give same result`` (NonNull (args: string)) =
103+
let ``Tokens.ofString then Token.toString should give same result`` (NonNull (args: string)) =
104104
let trimmed = args.TrimEnd()
105-
let result = trimmed |> Token.ofString |> Token.toString
105+
let result = trimmed |> Tokens.ofString |> Tokens.toString
106106
result =! trimmed
107107

108108

109109
[<Fact>]
110-
let ``Token.ofString then Token.toString should give same result with empty quotes `` () =
110+
let ``Tokens.ofString then Token.toString should give same result with empty quotes `` () =
111111
let trimmed = "\"\""
112-
let result = trimmed |> Token.ofString |> Token.toString
112+
let result = trimmed |> Tokens.ofString |> Tokens.toString
113113
result =! trimmed
114114

‎tests/Fargo.Test/Usage.fs

Copy file name to clipboardExpand all lines: tests/Fargo.Test/Usage.fs
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ open System.Threading.Tasks
1111
let (=!) (actual:'a) (expected: 'a) = Differ.Assert(expected, actual )
1212

1313
let outUsage (p,c) input =
14-
let _,_,usages = p (Token.ofString input)
14+
let _,_,usages = p (Tokens.ofString input)
1515
Testing.withStdout(fun _ -> printHelp usages)
1616

1717
let outRun p input =

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.