Skip to content

Commit 26ed0ee

Browse files
committed
- Add new CLI options to f tool: --chars, --lines, and --no-numbers.
- Add tests for new `f` tool features.
1 parent 7763292 commit 26ed0ee

File tree

7 files changed

+165
-22
lines changed

7 files changed

+165
-22
lines changed

Makefile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,20 @@
22

33
build:
44
dotnet build tools/ancp/f/f.fsproj -c Release -v minimal
5-
dotnet build tools/ancp/fc/fc.fsproj -c Release -v minimal
5+
dotnet build tools/ancp/fcc/fcc.fsproj -c Release -v minimal
66

77
install: build
88
(dotnet tool install --global --add-source ./tools/ancp/f/nupkg ancp \
99
|| dotnet tool update --global --add-source ./tools/ancp/f/nupkg ancp) || true
10-
(dotnet tool install --global --add-source ./tools/ancp/fc/nupkg --prerelease ancp.fc \
11-
|| dotnet tool update --global --add-source ./tools/ancp/fc/nupkg --prerelease ancp.fc) || true
10+
(dotnet tool install --global --add-source ./tools/ancp/fcc/nupkg ancp.fcc \
11+
|| dotnet tool update --global --add-source ./tools/ancp/fcc/nupkg ancp.fcc) || true
1212

1313
test:
1414
dotnet test tests/f.tests/f.tests.fsproj -v minimal
15-
dotnet test tests/fc.tests/fc.tests.fsproj -v minimal
15+
dotnet test tests/fcc.tests/fcc.tests.fsproj -v minimal
1616

1717
clean:
1818
rm -rf tools/ancp/f/bin tools/ancp/f/obj tools/ancp/f/nupkg \
19-
tools/ancp/fc/bin tools/ancp/fc/obj tools/ancp/fc/nupkg \
19+
tools/ancp/fcc/bin tools/ancp/fcc/obj tools/ancp/fcc/nupkg \
2020
tests/f.tests/bin tests/f.tests/obj \
21-
tests/fc.tests/bin tests/fc.tests/obj || true
21+
tests/fcc.tests/bin tests/fcc.tests/obj || true

scripts/install_tools.ps1

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ $ErrorActionPreference = 'Stop'
33

44
Write-Host "Building tools (Release)..."
55
dotnet build tools/ancp/f/f.fsproj -c Release -v minimal
6-
dotnet build tools/ancp/fc/fc.fsproj -c Release -v minimal
6+
dotnet build tools/ancp/fcc/fcc.fsproj -c Release -v minimal
77

88
Write-Host "Installing/updating global tools..."
99
dotnet tool install --global --add-source ./tools/ancp/f/nupkg ancp 2>$null \
1010
; if ($LASTEXITCODE -ne 0) { dotnet tool update --global --add-source ./tools/ancp/f/nupkg ancp }
1111

12-
dotnet tool install --global --add-source ./tools/ancp/fc/nupkg --prerelease ancp.fc 2>$null \
13-
; if ($LASTEXITCODE -ne 0) { dotnet tool update --global --add-source ./tools/ancp/fc/nupkg --prerelease ancp.fc }
12+
dotnet tool install --global --add-source ./tools/ancp/fcc/nupkg ancp.fcc 2>$null \
13+
; if ($LASTEXITCODE -ne 0) { dotnet tool update --global --add-source ./tools/ancp/fcc/nupkg ancp.fcc }
1414

15-
Write-Host "Done. Commands available: f, fc"
15+
Write-Host "Done. Commands available: f, fcc"

scripts/install_tools.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ set -euo pipefail
33

44
echo "Building tools (Release)..."
55
dotnet build tools/ancp/f/f.fsproj -c Release -v minimal
6-
dotnet build tools/ancp/fc/fc.fsproj -c Release -v minimal
6+
dotnet build tools/ancp/fcc/fcc.fsproj -c Release -v minimal
77

88
echo "Installing/updating global tools..."
99
dotnet tool install --global --add-source ./tools/ancp/f/nupkg ancp \
1010
|| dotnet tool update --global --add-source ./tools/ancp/f/nupkg ancp
1111

12-
dotnet tool install --global --add-source ./tools/ancp/fc/nupkg --prerelease ancp.fc \
13-
|| dotnet tool update --global --add-source ./tools/ancp/fc/nupkg --prerelease ancp.fc
12+
dotnet tool install --global --add-source ./tools/ancp/fcc/nupkg ancp.fcc \
13+
|| dotnet tool update --global --add-source ./tools/ancp/fcc/nupkg ancp.fcc
1414

15-
echo "Done. Commands available: f, fc"
15+
echo "Done. Commands available: f, fcc"

tests/f.tests/FToolTests.fs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,3 +275,128 @@ type FToolUnitTests() =
275275

276276
Assert.That(ecMissing, Is.EqualTo(1))
277277
Assert.That(errMissing.Contains("Error:"), Is.True)
278+
279+
[<Test>]
280+
member _.``--chars option counts characters``() =
281+
let tmp = Path.Combine(Path.GetTempPath(), "f_chars_" + Guid.NewGuid().ToString("N"))
282+
Directory.CreateDirectory(tmp) |> ignore
283+
284+
try
285+
let testFile = Path.Combine(tmp, "test.txt")
286+
let content = "Hello World!\nTest 123"
287+
File.WriteAllText(testFile, content)
288+
289+
let repoRoot =
290+
let rec findUp (startDir: string) (marker: string) =
291+
let full = Path.GetFullPath(startDir)
292+
let candidate = Path.Combine(full, marker)
293+
if File.Exists(candidate) then
294+
full
295+
else
296+
let parent = Directory.GetParent(full)
297+
if isNull parent then
298+
failwithf $"Could not locate '%s{marker}' above '%s{startDir}'"
299+
else
300+
findUp parent.FullName marker
301+
findUp AppContext.BaseDirectory "ancpdevkit.sln"
302+
303+
let proj = Path.Combine(repoRoot, "tools", "ancp", "f", "f.fsproj")
304+
let psi = ProcessStartInfo("dotnet", $"run --no-build --project {proj} --framework net9.0 -- --chars {testFile}")
305+
psi.WorkingDirectory <- repoRoot
306+
psi.RedirectStandardOutput <- true
307+
psi.RedirectStandardError <- true
308+
use p = Process.Start(psi)
309+
let output = p.StandardOutput.ReadToEnd()
310+
p.WaitForExit()
311+
312+
Assert.That(p.ExitCode, Is.EqualTo(0))
313+
Assert.That(output.Trim(), Is.EqualTo(content.Length.ToString()))
314+
finally
315+
try Directory.Delete(tmp, true) with _ -> ()
316+
317+
[<Test>]
318+
member _.``--lines option counts lines``() =
319+
let tmp = Path.Combine(Path.GetTempPath(), "f_lines_" + Guid.NewGuid().ToString("N"))
320+
Directory.CreateDirectory(tmp) |> ignore
321+
322+
try
323+
let testFile = Path.Combine(tmp, "test.txt")
324+
let content = "Line 1\nLine 2\nLine 3"
325+
File.WriteAllText(testFile, content)
326+
327+
let repoRoot =
328+
let rec findUp (startDir: string) (marker: string) =
329+
let full = Path.GetFullPath(startDir)
330+
let candidate = Path.Combine(full, marker)
331+
if File.Exists(candidate) then
332+
full
333+
else
334+
let parent = Directory.GetParent(full)
335+
if isNull parent then
336+
failwithf $"Could not locate '%s{marker}' above '%s{startDir}'"
337+
else
338+
findUp parent.FullName marker
339+
findUp AppContext.BaseDirectory "ancpdevkit.sln"
340+
341+
let proj = Path.Combine(repoRoot, "tools", "ancp", "f", "f.fsproj")
342+
let psi = ProcessStartInfo("dotnet", $"run --no-build --project {proj} --framework net9.0 -- --lines {testFile}")
343+
psi.WorkingDirectory <- repoRoot
344+
psi.RedirectStandardOutput <- true
345+
psi.RedirectStandardError <- true
346+
use p = Process.Start(psi)
347+
let output = p.StandardOutput.ReadToEnd()
348+
p.WaitForExit()
349+
350+
Assert.That(p.ExitCode, Is.EqualTo(0))
351+
Assert.That(output.Trim(), Is.EqualTo("3"))
352+
finally
353+
try Directory.Delete(tmp, true) with _ -> ()
354+
355+
[<Test>]
356+
member _.``--no-numbers option excludes numeric words``() =
357+
let tmp = Path.Combine(Path.GetTempPath(), "f_no_nums_" + Guid.NewGuid().ToString("N"))
358+
Directory.CreateDirectory(tmp) |> ignore
359+
360+
try
361+
let testFile = Path.Combine(tmp, "test.txt")
362+
let content = "Hello 123 World 456 Test"
363+
File.WriteAllText(testFile, content)
364+
365+
let repoRoot =
366+
let rec findUp (startDir: string) (marker: string) =
367+
let full = Path.GetFullPath(startDir)
368+
let candidate = Path.Combine(full, marker)
369+
if File.Exists(candidate) then
370+
full
371+
else
372+
let parent = Directory.GetParent(full)
373+
if isNull parent then
374+
failwithf $"Could not locate '%s{marker}' above '%s{startDir}'"
375+
else
376+
findUp parent.FullName marker
377+
findUp AppContext.BaseDirectory "ancpdevkit.sln"
378+
379+
let proj = Path.Combine(repoRoot, "tools", "ancp", "f", "f.fsproj")
380+
381+
let psi1 = ProcessStartInfo("dotnet", $"run --no-build --project {proj} --framework net9.0 -- {testFile}")
382+
psi1.WorkingDirectory <- repoRoot
383+
psi1.RedirectStandardOutput <- true
384+
psi1.RedirectStandardError <- true
385+
use p1 = Process.Start(psi1)
386+
let output1 = p1.StandardOutput.ReadToEnd()
387+
p1.WaitForExit()
388+
389+
let psi2 = ProcessStartInfo("dotnet", $"run --no-build --project {proj} --framework net9.0 -- --no-numbers {testFile}")
390+
psi2.WorkingDirectory <- repoRoot
391+
psi2.RedirectStandardOutput <- true
392+
psi2.RedirectStandardError <- true
393+
use p2 = Process.Start(psi2)
394+
let output2 = p2.StandardOutput.ReadToEnd()
395+
p2.WaitForExit()
396+
397+
Assert.That(p1.ExitCode, Is.EqualTo(0))
398+
Assert.That(p2.ExitCode, Is.EqualTo(0))
399+
Assert.That(output1.Trim(), Is.EqualTo("5"))
400+
Assert.That(output2.Trim(), Is.EqualTo("3"))
401+
finally
402+
try Directory.Delete(tmp, true) with _ -> ()

tools/ancp/f/Program.fs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -186,14 +186,20 @@ let readInput (pathArgs: string array) =
186186
let main argv =
187187
let unique = argv |> Array.exists ((=) "--unique")
188188
let stripOnly = argv |> Array.exists ((=) "--strip")
189+
let chars = argv |> Array.exists ((=) "--chars")
190+
let lines = argv |> Array.exists ((=) "--lines")
191+
let noNumbers = argv |> Array.exists ((=) "--no-numbers")
189192
let showHelp = argv |> Array.exists (fun a -> a = "--help" || a = "-h")
190193
let inputs = argv |> Array.filter (fun a -> not (a.StartsWith("--")))
191194

192195
if showHelp then
193-
printfn "Usage: f [--strip|--unique] <file>"
194-
printfn " --strip Output text with #/HTML comments removed"
195-
printfn " --unique Print unique word count (default is total)"
196-
printfn " <file> .txt/.md/.docx/.pdf (paths with spaces supported)"
196+
printfn "Usage: f [options] <file>"
197+
printfn " --strip Output text with #/HTML comments removed"
198+
printfn " --unique Print unique word count (default is total)"
199+
printfn " --chars Print character count instead of words"
200+
printfn " --lines Print line count instead of words"
201+
printfn " --no-numbers Exclude numbers from word count"
202+
printfn " <file> .txt/.md/.docx/.pdf (paths with spaces supported)"
197203
printfn "Notes: PDF extraction uses 'pdftotext' if available."
198204
0
199205
else
@@ -204,14 +210,26 @@ let main argv =
204210
if stripOnly then
205211
printf $"%s{stripped}"
206212
0
213+
elif chars then
214+
printfn $"%d{stripped.Length}"
215+
0
216+
elif lines then
217+
let lineCount = stripped.Split('\n').Length
218+
printfn $"%d{lineCount}"
219+
0
207220
else
208221
let tokens = tokenize stripped
222+
let filteredTokens =
223+
if noNumbers then
224+
tokens |> List.filter (fun w -> not (w |> Seq.forall Char.IsDigit))
225+
else
226+
tokens
209227

210228
let count =
211229
if unique then
212-
tokens |> List.distinct |> List.length
230+
filteredTokens |> List.distinct |> List.length
213231
else
214-
tokens.Length
232+
filteredTokens.Length
215233

216234
printfn $"%d{count}"
217235
0

tools/ancp/f/f.fsproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<PackAsTool>true</PackAsTool>
77
<ToolCommandName>f</ToolCommandName>
88
<PackageId>ancp</PackageId>
9-
<Version>0.3.0</Version>
9+
<Version>0.4.0</Version>
1010
<Authors>ANcpLua</Authors>
1111
<Description>Word counter for text/markdown/docx files with unique count and comment stripping options</Description>
1212
<PackageTags>word-counter;cli-tool;text-processing;unicode;markdown;docx;fsharp;dotnet-tool;text-analysis;document-processing</PackageTags>

tools/ancp/fcc/fcc.fsproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<ToolCommandName>fcc</ToolCommandName>
1111
<PackageId>ancp.fcc</PackageId>
1212
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
13-
<Version>0.3.0</Version>
13+
<Version>0.4.0</Version>
1414
<Authors>ANcpLua</Authors>
1515
<Description>Strip C# comments/regions using Roslyn; functional F# CLI</Description>
1616
<PackageTags>csharp;comment-remover;code-cleanup;roslyn;syntax-tree;dotnet-tool;fsharp;code-formatting;refactoring</PackageTags>

0 commit comments

Comments
 (0)