├── .gitignore
├── .paket
├── Paket.Restore.targets
├── paket.bootstrapper.exe
├── paket.exe
└── paket.targets
├── FsMenu.sln
├── README.md
├── RELEASE_NOTES.md
├── appveyor.yaml
├── build.cmd
├── build.fsx
├── build.sh
├── misc
├── README.md
├── sample.gif
├── sample2.gif
├── sample3.gif
├── sample4.gif
└── sample5.gif
├── paket.dependencies
├── paket.lock
├── src
└── FsMenu.Core
│ ├── AssemblyInfo.fs
│ ├── Colors.fs
│ ├── FsMenu.Core.fsproj
│ ├── FsMenu.fs
│ └── paket.references
└── tests
└── FsMenu.Console
├── App.config
├── FsMenu.Console.fsproj
├── Program.fs
└── paket.references
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # MSTest test Results
33 | [Tt]est[Rr]esult*/
34 | [Bb]uild[Ll]og.*
35 |
36 | # NUNIT
37 | *.VisualState.xml
38 | TestResult.xml
39 |
40 | # Build Results of an ATL Project
41 | [Dd]ebugPS/
42 | [Rr]eleasePS/
43 | dlldata.c
44 |
45 | # .NET Core
46 | project.lock.json
47 | project.fragment.lock.json
48 | artifacts/
49 | **/Properties/launchSettings.json
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # Visual Studio code coverage results
117 | *.coverage
118 | *.coveragexml
119 |
120 | # NCrunch
121 | _NCrunch_*
122 | .*crunch*.local.xml
123 | nCrunchTemp_*
124 |
125 | # MightyMoose
126 | *.mm.*
127 | AutoTest.Net/
128 |
129 | # Web workbench (sass)
130 | .sass-cache/
131 |
132 | # Installshield output folder
133 | [Ee]xpress/
134 |
135 | # DocProject is a documentation generator add-in
136 | DocProject/buildhelp/
137 | DocProject/Help/*.HxT
138 | DocProject/Help/*.HxC
139 | DocProject/Help/*.hhc
140 | DocProject/Help/*.hhk
141 | DocProject/Help/*.hhp
142 | DocProject/Help/Html2
143 | DocProject/Help/html
144 |
145 | # Click-Once directory
146 | publish/
147 |
148 | # Publish Web Output
149 | *.[Pp]ublish.xml
150 | *.azurePubxml
151 | # TODO: Comment the next line if you want to checkin your web deploy settings
152 | # but database connection strings (with potential passwords) will be unencrypted
153 | *.pubxml
154 | *.publishproj
155 |
156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
157 | # checkin your Azure Web App publish settings, but sensitive information contained
158 | # in these scripts will be unencrypted
159 | PublishScripts/
160 |
161 | # NuGet Packages
162 | *.nupkg
163 | # The packages folder can be ignored because of Package Restore
164 | **/packages/*
165 | # except build/, which is used as an MSBuild target.
166 | !**/packages/build/
167 | # Uncomment if necessary however generally it will be regenerated when needed
168 | #!**/packages/repositories.config
169 | # NuGet v3's project.json files produces more ignorable files
170 | *.nuget.props
171 | *.nuget.targets
172 |
173 | # Microsoft Azure Build Output
174 | csx/
175 | *.build.csdef
176 |
177 | # Microsoft Azure Emulator
178 | ecf/
179 | rcf/
180 |
181 | # Windows Store app package directories and files
182 | AppPackages/
183 | BundleArtifacts/
184 | Package.StoreAssociation.xml
185 | _pkginfo.txt
186 |
187 | # Visual Studio cache files
188 | # files ending in .cache can be ignored
189 | *.[Cc]ache
190 | # but keep track of directories ending in .cache
191 | !*.[Cc]ache/
192 |
193 | # Others
194 | ClientBin/
195 | ~$*
196 | *~
197 | *.dbmdl
198 | *.dbproj.schemaview
199 | *.jfm
200 | *.pfx
201 | *.publishsettings
202 | orleans.codegen.cs
203 |
204 | # Since there are multiple workflows, uncomment next line to ignore bower_components
205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
206 | #bower_components/
207 |
208 | # RIA/Silverlight projects
209 | Generated_Code/
210 |
211 | # Backup & report files from converting an old project file
212 | # to a newer Visual Studio version. Backup files are not needed,
213 | # because we have git ;-)
214 | _UpgradeReport_Files/
215 | Backup*/
216 | UpgradeLog*.XML
217 | UpgradeLog*.htm
218 |
219 | # SQL Server files
220 | *.mdf
221 | *.ldf
222 | *.ndf
223 |
224 | # Business Intelligence projects
225 | *.rdl.data
226 | *.bim.layout
227 | *.bim_*.settings
228 |
229 | # Microsoft Fakes
230 | FakesAssemblies/
231 |
232 | # GhostDoc plugin setting file
233 | *.GhostDoc.xml
234 |
235 | # Node.js Tools for Visual Studio
236 | .ntvs_analysis.dat
237 | node_modules/
238 |
239 | # Typescript v1 declaration files
240 | typings/
241 |
242 | # Visual Studio 6 build log
243 | *.plg
244 |
245 | # Visual Studio 6 workspace options file
246 | *.opt
247 |
248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
249 | *.vbw
250 |
251 | # Visual Studio LightSwitch build output
252 | **/*.HTMLClient/GeneratedArtifacts
253 | **/*.DesktopClient/GeneratedArtifacts
254 | **/*.DesktopClient/ModelManifest.xml
255 | **/*.Server/GeneratedArtifacts
256 | **/*.Server/ModelManifest.xml
257 | _Pvt_Extensions
258 |
259 | # Paket dependency manager
260 | .paket/paket.exe
261 | paket-files/
262 |
263 | # FAKE - F# Make
264 | .fake/
265 |
266 | # JetBrains Rider
267 | .idea/
268 | *.sln.iml
269 |
270 | # CodeRush
271 | .cr/
272 |
273 | # Python Tools for Visual Studio (PTVS)
274 | __pycache__/
275 | *.pyc
276 |
277 | # Cake - Uncomment if you are using it
278 | # tools/**
279 | # !tools/packages.config
280 |
281 | # Telerik's JustMock configuration file
282 | *.jmconfig
283 |
284 | # BizTalk build output
285 | *.btp.cs
286 | *.btm.cs
287 | *.odx.cs
288 | *.xsd.cs
289 |
--------------------------------------------------------------------------------
/.paket/Paket.Restore.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
8 |
9 | true
10 | $(MSBuildThisFileDirectory)
11 | $(MSBuildThisFileDirectory)..\
12 | $(PaketRootPath)paket-files\paket.restore.cached
13 | $(PaketRootPath)paket.lock
14 | /Library/Frameworks/Mono.framework/Commands/mono
15 | mono
16 |
17 | $(PaketRootPath)paket.exe
18 | $(PaketToolsPath)paket.exe
19 | "$(PaketExePath)"
20 | $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)"
21 |
22 |
23 | <_PaketExeExtension>$([System.IO.Path]::GetExtension("$(PaketExePath)"))
24 | dotnet "$(PaketExePath)"
25 |
26 |
27 | "$(PaketExePath)"
28 |
29 | $(PaketRootPath)paket.bootstrapper.exe
30 | $(PaketToolsPath)paket.bootstrapper.exe
31 | "$(PaketBootStrapperExePath)"
32 | $(MonoPath) --runtime=v4.0.30319 "$(PaketBootStrapperExePath)"
33 |
34 |
35 |
36 |
37 | true
38 | true
39 |
40 |
41 |
42 |
43 |
44 |
45 | true
46 | $(NoWarn);NU1603
47 |
48 |
49 |
50 |
51 | /usr/bin/shasum $(PaketRestoreCacheFile) | /usr/bin/awk '{ print $1 }'
52 | /usr/bin/shasum $(PaketLockFilePath) | /usr/bin/awk '{ print $1 }'
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | $([System.IO.File]::ReadAllText('$(PaketRestoreCacheFile)'))
66 | $([System.IO.File]::ReadAllText('$(PaketLockFilePath)'))
67 | true
68 | false
69 | true
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | $(MSBuildProjectDirectory)\obj\$(MSBuildProjectFile).paket.references.cached
79 |
80 | $(MSBuildProjectFullPath).paket.references
81 |
82 | $(MSBuildProjectDirectory)\$(MSBuildProjectName).paket.references
83 |
84 | $(MSBuildProjectDirectory)\paket.references
85 | $(MSBuildProjectDirectory)\obj\$(MSBuildProjectFile).$(TargetFramework).paket.resolved
86 | true
87 | references-file-or-cache-not-found
88 |
89 |
90 |
91 |
92 | $([System.IO.File]::ReadAllText('$(PaketReferencesCachedFilePath)'))
93 | $([System.IO.File]::ReadAllText('$(PaketOriginalReferencesFilePath)'))
94 | references-file
95 | false
96 |
97 |
98 |
99 |
100 | false
101 |
102 |
103 |
104 |
105 | true
106 | target-framework '$(TargetFramework)'
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[0])
124 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[1])
125 | $([System.String]::Copy('%(PaketReferencesFileLines.Identity)').Split(',')[4])
126 |
127 |
128 | %(PaketReferencesFileLinesInfo.PackageVersion)
129 | All
130 |
131 |
132 |
133 |
134 | $(MSBuildProjectDirectory)/obj/$(MSBuildProjectFile).paket.clitools
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 | $([System.String]::Copy('%(PaketCliToolFileLines.Identity)').Split(',')[0])
144 | $([System.String]::Copy('%(PaketCliToolFileLines.Identity)').Split(',')[1])
145 |
146 |
147 | %(PaketCliToolFileLinesInfo.PackageVersion)
148 |
149 |
150 |
151 |
155 |
156 |
157 |
158 |
159 |
160 | false
161 |
162 |
163 |
164 |
165 |
166 | <_NuspecFilesNewLocation Include="$(BaseIntermediateOutputPath)$(Configuration)\*.nuspec"/>
167 |
168 |
169 |
170 | $(MSBuildProjectDirectory)/$(MSBuildProjectFile)
171 | true
172 | false
173 | true
174 | $(BaseIntermediateOutputPath)$(Configuration)
175 | $(BaseIntermediateOutputPath)
176 |
177 |
178 |
179 | <_NuspecFiles Include="$(AdjustedNuspecOutputPath)\*.nuspec"/>
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
232 |
233 |
274 |
275 |
276 |
277 |
--------------------------------------------------------------------------------
/.paket/paket.bootstrapper.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaiw/FsMenu/4f52cf9747675d15e1e0994b5775227d9e708475/.paket/paket.bootstrapper.exe
--------------------------------------------------------------------------------
/.paket/paket.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaiw/FsMenu/4f52cf9747675d15e1e0994b5775227d9e708475/.paket/paket.exe
--------------------------------------------------------------------------------
/.paket/paket.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | true
7 | $(MSBuildThisFileDirectory)
8 | $(MSBuildThisFileDirectory)..\
9 | /Library/Frameworks/Mono.framework/Commands/mono
10 | mono
11 |
12 |
13 |
14 |
15 | $(PaketRootPath)paket.exe
16 | $(PaketToolsPath)paket.exe
17 | "$(PaketExePath)"
18 | $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)"
19 |
20 |
21 |
22 |
23 |
24 | $(MSBuildProjectFullPath).paket.references
25 |
26 |
27 |
28 |
29 | $(MSBuildProjectDirectory)\$(MSBuildProjectName).paket.references
30 |
31 |
32 |
33 |
34 | $(MSBuildProjectDirectory)\paket.references
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | $(PaketCommand) restore --references-files "$(PaketReferences)"
47 |
48 | RestorePackages; $(BuildDependsOn);
49 |
50 |
51 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/FsMenu.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26430.15
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{D23FE473-F7D0-4C29-8BDD-18BCDE8491A6}"
7 | ProjectSection(SolutionItems) = preProject
8 | paket.dependencies = paket.dependencies
9 | paket.lock = paket.lock
10 | EndProjectSection
11 | EndProject
12 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FsMenu.Core", "src\FsMenu.Core\FsMenu.Core.fsproj", "{B1E153E6-8A13-4D07-81F5-161FD571C1BE}"
13 | EndProject
14 | Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FsMenu.Console", "tests\FsMenu.Console\FsMenu.Console.fsproj", "{EA938605-9ECD-45E1-BFEF-8F6B1F5AEFA4}"
15 | EndProject
16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D5BCD177-432F-4089-B156-7FA1FD8EDA05}"
17 | EndProject
18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{982A1C3A-0329-4D75-A083-1EADEF93A56F}"
19 | EndProject
20 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{44DB43C9-DF99-4A51-89DA-3C42A1D57F5C}"
21 | ProjectSection(SolutionItems) = preProject
22 | appveyor.yaml = appveyor.yaml
23 | build.cmd = build.cmd
24 | build.fsx = build.fsx
25 | build.sh = build.sh
26 | README.md = README.md
27 | RELEASE_NOTES.md = RELEASE_NOTES.md
28 | EndProjectSection
29 | EndProject
30 | Global
31 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
32 | Debug|Any CPU = Debug|Any CPU
33 | Release|Any CPU = Release|Any CPU
34 | EndGlobalSection
35 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
36 | {B1E153E6-8A13-4D07-81F5-161FD571C1BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37 | {B1E153E6-8A13-4D07-81F5-161FD571C1BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
38 | {B1E153E6-8A13-4D07-81F5-161FD571C1BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
39 | {B1E153E6-8A13-4D07-81F5-161FD571C1BE}.Release|Any CPU.Build.0 = Release|Any CPU
40 | {EA938605-9ECD-45E1-BFEF-8F6B1F5AEFA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {EA938605-9ECD-45E1-BFEF-8F6B1F5AEFA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {EA938605-9ECD-45E1-BFEF-8F6B1F5AEFA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {EA938605-9ECD-45E1-BFEF-8F6B1F5AEFA4}.Release|Any CPU.Build.0 = Release|Any CPU
44 | EndGlobalSection
45 | GlobalSection(SolutionProperties) = preSolution
46 | HideSolutionNode = FALSE
47 | EndGlobalSection
48 | GlobalSection(NestedProjects) = preSolution
49 | {B1E153E6-8A13-4D07-81F5-161FD571C1BE} = {D5BCD177-432F-4089-B156-7FA1FD8EDA05}
50 | {EA938605-9ECD-45E1-BFEF-8F6B1F5AEFA4} = {982A1C3A-0329-4D75-A083-1EADEF93A56F}
51 | EndGlobalSection
52 | EndGlobal
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FsMenu
2 |
3 | > A very small DSL to create an interactive cli.
4 |
5 | ### Build status
6 |
7 | **Master Branch**
8 |
9 | [](https://ci.appveyor.com/project/Jallah/fsmenu/branch/master)
10 |
11 | ### Nuget
12 |
13 | Inc.
14 |
15 | ### Usage
16 |
17 | #### The DSL:
18 |
19 | ```fsharp
20 | // Creates a new menu
21 | let Menu = Sub
22 |
23 | // Render sub menu
24 | let (+>) name entry : (string * MenuEntry) = (name,entry)
25 |
26 | // Execute action and exit
27 | let (=>) s f = (s, Action (fun () -> f(); Exit))
28 |
29 | // Execute action and render the previous menu
30 | let (<+) s f = (s, Action (fun () -> f(); NavigateBack))
31 |
32 | // Execute action and render the menu where you come frome
33 | let (<+=) s f = (s, Action (fun () -> f(); Stay))
34 | ```
35 |
36 | #### Example:
37 |
38 | ```fsharp
39 |
40 | let testFunc() =
41 | printfn "selected Sub Sub 3"
42 | printf "handle some input: "
43 | let input = Console.ReadLine()
44 | // Do some stuff with input
45 | ()
46 |
47 | printfn "Use Keys: UP-Arrow DOWN-Arrow ENTER BACK-SPACE\n"
48 |
49 | let mutable yesOrNo = ""
50 |
51 | let test =
52 | Menu [
53 | "Item 1" => (fun () -> printf "selected Item 1")
54 | "Item 2" +>
55 | Menu [
56 | "Sub 1" +>
57 | Menu [
58 | "Sub Sub 1" => (fun () -> printf "selected Sub Sub 1")
59 | "Sub Sub 2" +>
60 | Menu [
61 | "yes" <+ (fun () -> yesOrNo <- "--yes")
62 | "no " <+ (fun () -> yesOrNo <- "--no") ]
63 | "Sub Sub 3" <+ testFunc]
64 | "Sub 2" => (fun () -> printf "selected Sub 2")
65 | "Sub 3" => (fun () -> printf "exec some command with param %s" yesOrNo)]]
66 |
67 |
68 | render test "<--"
69 | ```
70 |
71 | > Instead of `(fun () -> printf ...` you could pass any `unit -> unit` function.
72 |
73 |
74 | #### Will turn into
75 |
76 | 
77 |
78 | #### Want some color ? :)
79 |
80 | ```fsharp
81 | renderWithColoredEmphaziser test "<--" Color.Green
82 | ```
83 |
84 | #### Here it is
85 |
86 | 
87 |
88 | ### It is also possible to emphazise the entry
89 |
90 | ```fsharp
91 | renderWithColoredEntry test Color.Cyan
92 | ```
93 |
94 | 
95 |
96 | ### Or Both, the emphaziser and the entry
97 |
98 | ```fsharp
99 | renderWithColoredLine test "<--" Color.Green
100 | ```
101 |
102 | 
103 |
104 | #### Another example
105 |
106 | [Here](https://github.com/nicolaiw/FsMenu/tree/master/misc)
107 |
108 | ### Build
109 |
110 | + On Windows run build.cmd
111 | + On Linux run build.sh
112 |
113 | ### TODO ( contributes are very welcome :D )
114 | + Add support for multiselect list and single select list
115 | + Create NuGet Package using Paket
116 | + Clean build.fsx
117 | + Refactoring: The goal is too use the render funcion like `render test <| withColoredEmphaziser "<--" Color.Green` or something like that
118 |
--------------------------------------------------------------------------------
/RELEASE_NOTES.md:
--------------------------------------------------------------------------------
1 | #### 0.0.1
2 | * Use Paket for dependency management
3 | * Use Fake (F# Make) for builds
4 | * Add appveyor build status
5 |
6 | #### 0.0.2
7 | * Posibility to colorize the emphazizer
8 | * Posibility to colorize the entry
9 | * Rename namspaces and modules
10 |
11 | #### 0.3.0
12 | * Wrape renderColored with different functions
13 | * Rendering with a colored emphaziser can now be accomplished using renderWithColoredEmphaziser
14 |
15 |
--------------------------------------------------------------------------------
/appveyor.yaml:
--------------------------------------------------------------------------------
1 | init:
2 | - git config --global core.autocrlf input
3 | build_script:
4 | - cmd: build.cmd
5 | test: off
6 | version: 0.0.1.{build}
7 | artifacts:
8 | - path: bin
9 | name: bin
--------------------------------------------------------------------------------
/build.cmd:
--------------------------------------------------------------------------------
1 | @echo off
2 | cls
3 |
4 | .paket\paket.bootstrapper.exe
5 | if errorlevel 1 (
6 | exit /b %errorlevel%
7 | )
8 |
9 | .paket\paket.exe restore
10 | if errorlevel 1 (
11 | exit /b %errorlevel%
12 | )
13 |
14 | IF NOT EXIST build.fsx (
15 | .paket\paket.exe update
16 | packages\FAKE\tools\FAKE.exe init.fsx
17 | )
18 | packages\FAKE\tools\FAKE.exe build.fsx %*
--------------------------------------------------------------------------------
/build.fsx:
--------------------------------------------------------------------------------
1 |
2 | // --------------------------------------------------------------------------------------
3 | // FAKE build script
4 | // --------------------------------------------------------------------------------------
5 |
6 | #r @"packages/FAKE/tools/FakeLib.dll"
7 | open Fake
8 | open Fake.Git
9 | open Fake.AssemblyInfoFile
10 | open Fake.ReleaseNotesHelper
11 | open System
12 | open System.IO
13 | //#if MONO
14 | //#else
15 | //#load "packages/SourceLink.Fake/tools/Fake.fsx"
16 | //open SourceLink
17 | //#endif
18 |
19 |
20 |
21 | // --------------------------------------------------------------------------------------
22 | // START TODO: Provide project-specific details below
23 | // --------------------------------------------------------------------------------------
24 |
25 | // Information about the project are used
26 | // - for version and project name in generated AssemblyInfo file
27 | // - by the generated NuGet package
28 | // - to run tests and to publish documentation on GitHub gh-pages
29 | // - for documentation, you also need to edit info in "docs/tools/generate.fsx"
30 |
31 | // The name of the project
32 | // (used by attributes in AssemblyInfo, name of a NuGet package and directory in 'src')
33 | let project = "FsMenu"
34 |
35 | // Short summary of the project
36 | // (used as description in AssemblyInfo and as a short summary for NuGet package)
37 | let summary = "Small DSL to create an interactive cli"
38 |
39 | // Longer description of the project
40 | // (used as a description for NuGet package; line breaks are automatically cleaned up)
41 | let description = "Small DSL to create an interactive cli"
42 |
43 | // List of author names (for NuGet package)
44 | let authors = [ "Wirtz N." ]
45 |
46 | // Tags for your project (for NuGet package)
47 | let tags = "FsMenu F# CLI cli"
48 |
49 | // File system information
50 | let solutionFile = "FsMenu.sln"
51 |
52 | // Pattern specifying assemblies to be tested using NUnit
53 | let testAssemblies = "tests/**/bin/Release/*Tests*.dll"
54 |
55 | // Git configuration (used for publishing documentation in gh-pages branch)
56 | // The profile where the project is posted
57 | let gitOwner = "nicolaiw"
58 | let gitHome = "https://github.com/" + gitOwner
59 |
60 | // The name of the project on GitHub
61 | let gitName = "FsMenu"
62 |
63 | // The url for the raw files hosted
64 | let gitRaw = environVarOrDefault "gitRaw" "https://raw.github.com/nicolaiw"
65 |
66 | // --------------------------------------------------------------------------------------
67 | // END TODO: The rest of the file includes standard build steps
68 | // --------------------------------------------------------------------------------------
69 |
70 | // Read additional information from the release notes document
71 | let release = LoadReleaseNotes "RELEASE_NOTES.md"
72 |
73 | // Helper active pattern for project types
74 | let (|Fsproj|Csproj|Vbproj|) (projFileName:string) =
75 | match projFileName with
76 | | f when f.EndsWith("fsproj") -> Fsproj
77 | | f when f.EndsWith("csproj") -> Csproj
78 | | f when f.EndsWith("vbproj") -> Vbproj
79 | | _ -> failwith (sprintf "Project file %s not supported. Unknown project type." projFileName)
80 |
81 | // Generate assembly info files with the right version & up-to-date information
82 | Target "AssemblyInfo" (fun _ ->
83 | let getAssemblyInfoAttributes projectName =
84 | [ Attribute.Title (projectName)
85 | Attribute.Product project
86 | Attribute.Description summary
87 | Attribute.Version release.AssemblyVersion
88 | Attribute.FileVersion release.AssemblyVersion]
89 |
90 | let getProjectDetails projectPath =
91 | let projectName = System.IO.Path.GetFileNameWithoutExtension(projectPath)
92 | ( projectPath,
93 | projectName,
94 | System.IO.Path.GetDirectoryName(projectPath),
95 | (getAssemblyInfoAttributes projectName)
96 | )
97 |
98 | !! "src/**/*.??proj"
99 | |> Seq.map getProjectDetails
100 | |> Seq.iter (fun (projFileName, projectName, folderName, attributes) ->
101 | match projFileName with
102 | | Fsproj -> CreateFSharpAssemblyInfo (folderName @@ "AssemblyInfo.fs") attributes
103 | | Csproj -> CreateCSharpAssemblyInfo ((folderName @@ "Properties") @@ "AssemblyInfo.cs") attributes
104 | | Vbproj -> CreateVisualBasicAssemblyInfo ((folderName @@ "My Project") @@ "AssemblyInfo.vb") attributes
105 | )
106 | )
107 |
108 | // Copies binaries from default VS location to expected bin folder
109 | // But keeps a subdirectory structure for each project in the
110 | // src folder to support multiple project outputs
111 | Target "CopyBinaries" (fun _ ->
112 | !! "src/**/*.??proj"
113 | |> Seq.map (fun f -> ((System.IO.Path.GetDirectoryName f) @@ "bin/Release", "bin" @@ (System.IO.Path.GetFileNameWithoutExtension f)))
114 | |> Seq.iter (fun (fromDir, toDir) -> CopyDir toDir fromDir (fun _ -> true))
115 | )
116 |
117 | // --------------------------------------------------------------------------------------
118 | // Clean build results
119 |
120 | Target "Clean" (fun _ ->
121 | CleanDirs ["bin"; "temp"]
122 | )
123 |
124 | Target "CleanDocs" (fun _ ->
125 | CleanDirs ["docs/output"]
126 | )
127 |
128 | // --------------------------------------------------------------------------------------
129 | // Build library & test project
130 |
131 | #if MONO
132 | traceImportant "MONO"
133 |
134 | Target "Build" (fun _ ->
135 | !! solutionFile
136 | //|> MSBuildRelease "" "Rebuild"
137 | |> MSBuild "" "Rebuild" ([
138 | ("Configuration", "Release");
139 | ("DefineConstants", "MONO")
140 | ])
141 | |> ignore
142 | )
143 | #else
144 | traceImportant "WIN"
145 |
146 | Target "Build" (fun _ ->
147 |
148 | !! solutionFile
149 | //|> MSBuildRelease "" "Rebuild"
150 | |> MSBuild "" "Rebuild" ([
151 | ("Configuration", "Release");
152 | ("DefineConstants", "WIN")
153 | ])
154 | |> ignore
155 | )
156 | #endif
157 |
158 | // --------------------------------------------------------------------------------------
159 | // Run the unit tests using test runner
160 |
161 | Target "RunTests" (fun _ ->
162 | !! testAssemblies
163 | |> NUnit (fun p ->
164 | { p with
165 | DisableShadowCopy = true
166 | TimeOut = TimeSpan.FromMinutes 20.
167 | OutputFile = "TestResults.xml" })
168 | )
169 |
170 | #if MONO
171 | #else
172 | // --------------------------------------------------------------------------------------
173 | // SourceLink allows Source Indexing on the PDB generated by the compiler, this allows
174 | // the ability to step through the source code of external libraries https://github.com/ctaggart/SourceLink
175 |
176 | //Target "SourceLink" (fun _ ->
177 | // let baseUrl = sprintf "%s/%s/{0}/%%var2%%" gitRaw (project.ToLower())
178 | // use repo = new GitRepo(__SOURCE_DIRECTORY__)
179 |
180 | // let addAssemblyInfo (projFileName:String) =
181 | // match projFileName with
182 | // | Fsproj -> (projFileName, "**/AssemblyInfo.fs")
183 | // | Csproj -> (projFileName, "**/AssemblyInfo.cs")
184 | // | Vbproj -> (projFileName, "**/AssemblyInfo.vb")
185 |
186 | // !! "src/**/*.??proj"
187 | // |> Seq.map addAssemblyInfo
188 | // |> Seq.iter (fun (projFile, assemblyInfo) ->
189 | // let proj = VsProj.LoadRelease projFile
190 | // logfn "source linking %s" proj.OutputFilePdb
191 | // let files = proj.CompilesNotLinked -- assemblyInfo
192 | // repo.VerifyChecksums files
193 | // proj.VerifyPdbChecksums files
194 | // proj.CreateSrcSrv baseUrl repo.Commit (repo.Paths files)
195 | // Pdbstr.exec proj.OutputFilePdb proj.OutputFilePdbSrcSrv
196 | // )
197 | //)
198 |
199 | #endif
200 |
201 | // --------------------------------------------------------------------------------------
202 | // Build a NuGet package
203 |
204 | Target "NuGet" (fun _ ->
205 | Paket.Pack(fun p ->
206 | { p with
207 | OutputPath = "bin"
208 | Version = release.NugetVersion
209 | ReleaseNotes = toLines release.Notes})
210 | )
211 |
212 | Target "PublishNuget" (fun _ ->
213 | Paket.Push(fun p ->
214 | { p with
215 | WorkingDir = "bin" })
216 | )
217 |
218 |
219 | // --------------------------------------------------------------------------------------
220 | // Generate the documentation
221 |
222 | Target "GenerateReferenceDocs" (fun _ ->
223 | if not <| executeFSIWithArgs "docs/tools" "generate.fsx" ["--define:RELEASE"; "--define:REFERENCE"] [] then
224 | failwith "generating reference documentation failed"
225 | )
226 |
227 | let generateHelp' fail debug =
228 | let args =
229 | if debug then ["--define:HELP"]
230 | else ["--define:RELEASE"; "--define:HELP"]
231 | if executeFSIWithArgs "docs/tools" "generate.fsx" args [] then
232 | traceImportant "Help generated"
233 | else
234 | if fail then
235 | failwith "generating help documentation failed"
236 | else
237 | traceImportant "generating help documentation failed"
238 |
239 | let generateHelp fail =
240 | generateHelp' fail false
241 |
242 | Target "GenerateHelp" (fun _ ->
243 | DeleteFile "docs/content/release-notes.md"
244 | CopyFile "docs/content/" "RELEASE_NOTES.md"
245 | Rename "docs/content/release-notes.md" "docs/content/RELEASE_NOTES.md"
246 |
247 | DeleteFile "docs/content/license.md"
248 | CopyFile "docs/content/" "LICENSE.txt"
249 | Rename "docs/content/license.md" "docs/content/LICENSE.txt"
250 |
251 | generateHelp true
252 | )
253 |
254 | Target "GenerateHelpDebug" (fun _ ->
255 | DeleteFile "docs/content/release-notes.md"
256 | CopyFile "docs/content/" "RELEASE_NOTES.md"
257 | Rename "docs/content/release-notes.md" "docs/content/RELEASE_NOTES.md"
258 |
259 | DeleteFile "docs/content/license.md"
260 | CopyFile "docs/content/" "LICENSE.txt"
261 | Rename "docs/content/license.md" "docs/content/LICENSE.txt"
262 |
263 | generateHelp' true true
264 | )
265 |
266 | Target "KeepRunning" (fun _ ->
267 | use watcher = new FileSystemWatcher(DirectoryInfo("docs/content").FullName,"*.*")
268 | watcher.EnableRaisingEvents <- true
269 | watcher.Changed.Add(fun e -> generateHelp false)
270 | watcher.Created.Add(fun e -> generateHelp false)
271 | watcher.Renamed.Add(fun e -> generateHelp false)
272 | watcher.Deleted.Add(fun e -> generateHelp false)
273 |
274 | traceImportant "Waiting for help edits. Press any key to stop."
275 |
276 | System.Console.ReadKey() |> ignore
277 |
278 | watcher.EnableRaisingEvents <- false
279 | watcher.Dispose()
280 | )
281 |
282 | Target "GenerateDocs" DoNothing
283 |
284 | let createIndexFsx lang =
285 | let content = """(*** hide ***)
286 | // This block of code is omitted in the generated HTML documentation. Use
287 | // it to define helpers that you do not want to show in the documentation.
288 | #I "../../../bin"
289 |
290 | (**
291 | F# Project Scaffold ({0})
292 | =========================
293 | *)
294 | """
295 | let targetDir = "docs/content" @@ lang
296 | let targetFile = targetDir @@ "index.fsx"
297 | ensureDirectory targetDir
298 | System.IO.File.WriteAllText(targetFile, System.String.Format(content, lang))
299 |
300 | Target "AddLangDocs" (fun _ ->
301 | let args = System.Environment.GetCommandLineArgs()
302 | if args.Length < 4 then
303 | failwith "Language not specified."
304 |
305 | args.[3..]
306 | |> Seq.iter (fun lang ->
307 | if lang.Length <> 2 && lang.Length <> 3 then
308 | failwithf "Language must be 2 or 3 characters (ex. 'de', 'fr', 'ja', 'gsw', etc.): %s" lang
309 |
310 | let templateFileName = "template.cshtml"
311 | let templateDir = "docs/tools/templates"
312 | let langTemplateDir = templateDir @@ lang
313 | let langTemplateFileName = langTemplateDir @@ templateFileName
314 |
315 | if System.IO.File.Exists(langTemplateFileName) then
316 | failwithf "Documents for specified language '%s' have already been added." lang
317 |
318 | ensureDirectory langTemplateDir
319 | Copy langTemplateDir [ templateDir @@ templateFileName ]
320 |
321 | createIndexFsx lang)
322 | )
323 |
324 | // --------------------------------------------------------------------------------------
325 | // Release Scripts
326 |
327 | //Target "ReleaseDocs" (fun _ ->
328 | // let tempDocsDir = "temp/gh-pages"
329 | // CleanDir tempDocsDir
330 | // Repository.cloneSingleBranch "" (gitHome + "/" + gitName + ".git") "gh-pages" tempDocsDir
331 |
332 | // CopyRecursive "docs/output" tempDocsDir true |> tracefn "%A"
333 | // StageAll tempDocsDir
334 | // Git.Commit.Commit tempDocsDir (sprintf "Update generated documentation for version %s" release.NugetVersion)
335 | // Branches.push tempDocsDir
336 | //)
337 |
338 | //#load "paket-files/fsharp/FAKE/modules/Octokit/Octokit.fsx"
339 | //open Octokit
340 |
341 | Target "Release" DoNothing// (fun _ ->
342 | // StageAll ""
343 | // Git.Commit.Commit "" (sprintf "Bump version to %s" release.NugetVersion)
344 | // Branches.push ""
345 |
346 | // Branches.tag "" release.NugetVersion
347 | // Branches.pushTag "" "origin" release.NugetVersion
348 |
349 | // // release on github
350 | // createClient (getBuildParamOrDefault "github-user" "") (getBuildParamOrDefault "github-pw" "")
351 | // |> createDraft gitOwner gitName release.NugetVersion (release.SemVer.PreRelease <> None) release.Notes
352 | // // TODO: |> uploadFile "PATH_TO_FILE"
353 | // |> releaseDraft
354 | // |> Async.RunSynchronously
355 | //)
356 |
357 | Target "BuildPackage" DoNothing
358 |
359 | // --------------------------------------------------------------------------------------
360 | // Run all targets by default. Invoke 'build ' to override
361 |
362 | Target "All" DoNothing
363 | Target "Default" DoNothing
364 |
365 | "Clean"
366 | ==> "AssemblyInfo"
367 | ==> "Build"
368 | ==> "CopyBinaries"
369 | ==> "RunTests"
370 | // ==> "GenerateReferenceDocs" https://github.com/fsprojects/ProjectScaffold/issues/170
371 | // ==> "GenerateDocs"
372 | //==> "All"
373 | //=?> ("ReleaseDocs",isLocalBuild)
374 |
375 | "All"
376 | #if MONO
377 | #else
378 | //=?> ("SourceLink", Pdbstr.tryFind().IsSome )
379 | #endif
380 | //==> "NuGet"
381 | //==> "BuildPackage"
382 | ==> "Build"
383 |
384 | //"CleanDocs"
385 | // ==> "GenerateHelp"
386 | //// ==> "GenerateReferenceDocs" https://github.com/fsprojects/ProjectScaffold/issues/170
387 | //// ==> "GenerateDocs"
388 |
389 | //"CleanDocs"
390 | // ==> "GenerateHelpDebug"
391 |
392 | //"GenerateHelp"
393 | // ==> "KeepRunning"
394 |
395 | //"ReleaseDocs"
396 | // ==> "Release"
397 |
398 | //"BuildPackage"
399 | // ==> "PublishNuget"
400 | // ==> "Release"
401 |
402 | "Default"
403 | ==> "Build"
404 |
405 | RunTargetOrDefault "Build"
406 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -eu
4 | set -o pipefail
5 |
6 | cd `dirname $0`
7 |
8 | FSIARGS=""
9 | OS=${OS:-"unknown"}
10 | if [[ "$OS" != "Windows_NT" ]]
11 | then
12 | FSIARGS="--fsiargs -d:MONO"
13 | fi
14 |
15 | function run() {
16 | if [[ "$OS" != "Windows_NT" ]]
17 | then
18 | mono "$@"
19 | else
20 | "$@"
21 | fi
22 | }
23 |
24 | run .paket/paket.bootstrapper.exe
25 |
26 | if [[ "$OS" != "Windows_NT" ]] &&
27 | [ ! -e ~/.config/.mono/certs ]
28 | then
29 | mozroots --import --sync --quiet
30 | fi
31 |
32 | run .paket/paket.exe restore
33 |
34 | [ ! -e build.fsx ] && run .paket/paket.exe update
35 | [ ! -e build.fsx ] && run packages/FAKE/tools/FAKE.exe init.fsx
36 | run packages/FAKE/tools/FAKE.exe "$@" $FSIARGS build.fsx
--------------------------------------------------------------------------------
/misc/README.md:
--------------------------------------------------------------------------------
1 | ```fsharp
2 | open System.Diagnostics
3 | open System
4 | open FsMenu.Core
5 |
6 | let paketPath = System.IO.Path.Combine("..", "paket.exe")
7 |
8 | let paket args =
9 | let p = new Process()
10 | p.StartInfo.FileName <- paketPath
11 | p.StartInfo.CreateNoWindow <- false
12 | p.StartInfo.Arguments <- args
13 | p.StartInfo.UseShellExecute <- false
14 | p.StartInfo.RedirectStandardOutput <- true
15 | p.Start() |> ignore
16 |
17 | while not p.StandardOutput.EndOfStream do
18 | printfn "%s" (p.StandardOutput.ReadLine())
19 |
20 |
21 | let paketAddSubMenu =
22 | let mutable add = "add"
23 | let mutable name = ""
24 | let mutable force = ""
25 | let mutable verbose = ""
26 | // ...
27 |
28 | let yesNoMenu onYes onNo =
29 | Menu [
30 | "yes" <+ onYes
31 | "no " <+ onNo]
32 |
33 | let setName() = printf "name: "
34 | name <- Console.ReadLine()
35 |
36 | let setForceMenu =
37 | yesNoMenu (fun () -> force <- "--force") (fun () -> force <- "")
38 |
39 | let setVerboseMenu =
40 | yesNoMenu (fun () -> verbose <- "--verbose") (fun () -> verbose <- "")
41 |
42 | let runAddWithArgs() =
43 | [name
44 | force
45 | verbose]
46 | |> List.filter (fun arg -> arg <> "")
47 | |> List.fold (fun acc arg -> sprintf "%s %s" acc arg) add
48 | |> printf "%s"
49 |
50 |
51 | let men =
52 | Menu [
53 | "name " <+= setName
54 | "force " +> setForceMenu
55 | "verbose" +> setVerboseMenu
56 | "run_add" => runAddWithArgs]
57 | men
58 |
59 | []
60 | let main argv =
61 |
62 | printfn ""
63 |
64 | let test =
65 | Menu [
66 | "install " => (fun () -> paket "install" |> ignore)
67 | "update " => (fun () -> paket "update" |> ignore)
68 | "restore " => (fun () -> paket "restore" |> ignore)
69 | "add package" +> paketAddSubMenu ]
70 |
71 | render test "|> exec"
72 |
73 |
74 | Console.ReadLine() |> ignore
75 | 0
76 | ```
77 |
78 | 
--------------------------------------------------------------------------------
/misc/sample.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaiw/FsMenu/4f52cf9747675d15e1e0994b5775227d9e708475/misc/sample.gif
--------------------------------------------------------------------------------
/misc/sample2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaiw/FsMenu/4f52cf9747675d15e1e0994b5775227d9e708475/misc/sample2.gif
--------------------------------------------------------------------------------
/misc/sample3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaiw/FsMenu/4f52cf9747675d15e1e0994b5775227d9e708475/misc/sample3.gif
--------------------------------------------------------------------------------
/misc/sample4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaiw/FsMenu/4f52cf9747675d15e1e0994b5775227d9e708475/misc/sample4.gif
--------------------------------------------------------------------------------
/misc/sample5.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolaiw/FsMenu/4f52cf9747675d15e1e0994b5775227d9e708475/misc/sample5.gif
--------------------------------------------------------------------------------
/paket.dependencies:
--------------------------------------------------------------------------------
1 | source https://www.nuget.org/api/v2
2 | nuget Fake
3 | nuget System.ValueTuple 4.3.0 restriction: >= net452
--------------------------------------------------------------------------------
/paket.lock:
--------------------------------------------------------------------------------
1 | NUGET
2 | remote: https://www.nuget.org/api/v2
3 | FAKE (4.62.6)
4 | System.ValueTuple (4.3) - restriction: >= net452
5 |
--------------------------------------------------------------------------------
/src/FsMenu.Core/AssemblyInfo.fs:
--------------------------------------------------------------------------------
1 | // Auto-Generated by FAKE; do not edit
2 | namespace System
3 | open System.Reflection
4 |
5 | []
6 | []
7 | []
8 | []
9 | []
10 | do ()
11 |
12 | module internal AssemblyVersionInformation =
13 | let [] AssemblyTitle = "FsMenu.Core"
14 | let [] AssemblyProduct = "FsMenu"
15 | let [] AssemblyDescription = "Small DSL to create an interactive cli"
16 | let [] AssemblyVersion = "0.3.0"
17 | let [] AssemblyFileVersion = "0.3.0"
18 |
--------------------------------------------------------------------------------
/src/FsMenu.Core/Colors.fs:
--------------------------------------------------------------------------------
1 | namespace FsMenu
2 |
3 |
4 | module Colors =
5 |
6 | type Color =
7 | | None = -1
8 | | Black = 0
9 | | DarkBlue = 1
10 | | DarkGreen = 2
11 | | DarkCyan = 3
12 | | DarkRed = 4
13 | | DarkMagenta = 5
14 | | DarkYellow = 6
15 | | Gray = 7
16 | | DarkGray = 8
17 | | Blue = 9
18 | | Green = 10
19 | | Cyan = 11
20 | | Red = 12
21 | | Magenta = 13
22 | | Yellow = 14
23 | | White = 15
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/FsMenu.Core/FsMenu.Core.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | 2.0
8 | b1e153e6-8a13-4d07-81f5-161fd571c1be
9 | Library
10 | FsMenu.Core
11 | FsMenu.Core
12 | v4.5.2
13 | 4.4.1.0
14 | true
15 | FsMenu.Core
16 |
17 |
18 | true
19 | full
20 | false
21 | false
22 | bin\$(Configuration)\
23 | DEBUG;TRACE
24 | 3
25 | bin\$(Configuration)\$(AssemblyName).XML
26 |
27 |
28 | pdbonly
29 | true
30 | true
31 | bin\$(Configuration)\
32 | TRACE
33 | 3
34 | bin\$(Configuration)\$(AssemblyName).XML
35 |
36 |
37 | 11
38 |
39 |
40 |
41 |
42 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets
43 |
44 |
45 |
46 |
47 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | FSharp.Core
62 | FSharp.Core.dll
63 | $(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\$(TargetFSharpCoreVersion)\FSharp.Core.dll
64 |
65 |
66 |
67 |
68 |
69 |
76 |
77 |
78 |
79 |
80 |
81 | ..\..\packages\System.ValueTuple\lib\netstandard1.0\System.ValueTuple.dll
82 | True
83 | True
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/src/FsMenu.Core/FsMenu.fs:
--------------------------------------------------------------------------------
1 |
2 | module FsMenu.Core
3 |
4 | type AfterExecution =
5 | | NavigateBack
6 | | Stay
7 | | Exit
8 |
9 | type MenuEntry =
10 | | Action of (unit -> AfterExecution)
11 | | Sub of (string * MenuEntry) list
12 |
13 | open System
14 | open FsMenu.Colors
15 |
16 | (* Small DSL *)
17 | let Menu = Sub
18 |
19 | // Render sub menu
20 | let (+>) name entry : (string * MenuEntry) = (name,entry)
21 |
22 | // Execute action and exit
23 | let (=>) s f = (s, Action (fun () -> f(); Exit))
24 |
25 | // Execute action and render the previous menu
26 | let (<+) s f = (s, Action (fun () -> f(); NavigateBack))
27 |
28 | // Execute action and render the menu where you come frome
29 | let (<+=) s f = (s, Action (fun () -> f(); Stay))
30 |
31 |
32 | // TODO:
33 | // 1. create Nuget package
34 |
35 |
36 | type private Colorize =
37 | | Emphasizer
38 | | Entry
39 | | Both
40 |
41 | (* Colorizes the emphasizer with a given color *)
42 | let private colorizeEmphasizer (oldIndex: int) (newIndex:int) (emphasizer: string) (color: Color) (entries: string list) =
43 |
44 | let inititalCursorPos = Console.CursorTop
45 |
46 | (* Clear the current emphasizer *)
47 | Console.SetCursorPosition(entries.[oldIndex].Length + 1, Console.CursorTop - (entries.Length-oldIndex))
48 | Console.Write(new string (' ', emphasizer.Length))
49 |
50 | (* Emphasise the new entry *)
51 | Console.SetCursorPosition(0, inititalCursorPos)
52 | Console.SetCursorPosition(entries.[newIndex].Length, Console.CursorTop - (entries.Length-newIndex))
53 |
54 | match color with
55 | | Color.None ->
56 | Console.Write(sprintf " %s" emphasizer)
57 |
58 | | c as consoleColor ->
59 | let currentForegroundColor = Console.ForegroundColor
60 | Console.ForegroundColor <- enum (unbox c)
61 | Console.Write(sprintf " %s" emphasizer)
62 | Console.ForegroundColor <- currentForegroundColor
63 |
64 | (* Reset the Cursor *)
65 | Console.SetCursorPosition(0, inititalCursorPos);
66 |
67 |
68 | (* Colorizes an entry *)
69 | let private colorizeEntry (oldIndex: int) (newIndex:int) (color: Color) (entries: string list) =
70 |
71 | let inititalCursorPos = Console.CursorTop
72 |
73 | (* Clear the current entry *)
74 | Console.SetCursorPosition(0, Console.CursorTop - (entries.Length-oldIndex))
75 | Console.Write(entries.[oldIndex])
76 |
77 | (* Emphasise the new entry *)
78 | Console.SetCursorPosition(0, inititalCursorPos)
79 |
80 | let currentForegroundColor = Console.ForegroundColor
81 |
82 | match color with
83 | | Color.None -> ()
84 |
85 | | c as consoleColor ->
86 |
87 | Console.ForegroundColor <- enum (unbox c)
88 | Console.SetCursorPosition(0, Console.CursorTop - (entries.Length-newIndex))
89 | Console.Write(sprintf "%s" entries.[newIndex])
90 | Console.ForegroundColor <- currentForegroundColor
91 |
92 | (* Reset the Cursor *)
93 | Console.SetCursorPosition(0, inititalCursorPos);
94 |
95 | (* Colorizes the whole line including the emphasizer *)
96 | let private colorizeLine (oldIndex: int) (newIndex:int) (emphasizer: string) (color: Color) (entries: string list) =
97 |
98 | // TODO: explicite colorization instead of calling both functions
99 | colorizeEntry oldIndex newIndex color entries
100 | colorizeEmphasizer oldIndex newIndex emphasizer color entries
101 |
102 |
103 | (* Emphasizes an entry *)
104 | let private emphasizeEntry (oldIndex: int) (newIndex:int) (emphasizer: string) (color: Color) (colorize: Colorize) (entries: string list) =
105 |
106 | match colorize with
107 | | Emphasizer -> colorizeEmphasizer oldIndex newIndex emphasizer color entries
108 | | Entry -> colorizeEntry oldIndex newIndex color entries
109 | | Both -> colorizeLine oldIndex newIndex emphasizer color entries
110 |
111 |
112 | (* Clears the current menu *)
113 | let private clear entryCount =
114 | let diff = Math.Abs (Console.CursorTop - entryCount)
115 |
116 | for i=0 to entryCount do
117 | Console.SetCursorPosition(0, i + diff);
118 | Console.Write(new string(' ', Console.WindowWidth))
119 |
120 | Console.SetCursorPosition(0, diff);
121 |
122 |
123 | (* Shows the menu and handles Input *)
124 | let private renderColored menuEntry emphesizer color colorize=
125 |
126 | (* All recursive stuff is hidden for the user of "render" *)
127 | let rec renderSubMenuRec callStack menuEntry emphasize =
128 | match menuEntry with
129 | | Sub (subMenu) ->
130 | for i = 0 to subMenu.Length-1 do
131 | let name = fst subMenu.[i]
132 | //let entry = if i = emphasize then sprintf "%s %s" name emphesizer else name
133 | printfn "%s" name;
134 |
135 | subMenu
136 | |> List.map fst
137 | |> emphasizeEntry 0 0 emphesizer color colorize
138 |
139 | let rec handleUserInput currentEntry =
140 | let cki = Console.ReadKey(true)
141 |
142 | match cki.Key with
143 | | ConsoleKey.DownArrow ->
144 | if currentEntry+1 < subMenu.Length then
145 | subMenu |> List.map fst |> emphasizeEntry currentEntry (currentEntry+1) emphesizer color colorize
146 | handleUserInput (currentEntry+1)
147 | else
148 | handleUserInput currentEntry
149 |
150 | | ConsoleKey.UpArrow ->
151 | if currentEntry-1 >= 0 then
152 | subMenu |> List.map fst |> emphasizeEntry currentEntry (currentEntry-1) emphesizer color colorize
153 | handleUserInput (currentEntry-1)
154 | else
155 | handleUserInput currentEntry
156 |
157 | | ConsoleKey.Enter ->
158 | (* Either call the action or navigate downwards *)
159 | match snd subMenu.[currentEntry] with
160 | | Action handler ->
161 | let cursorPosBeforeExecute = Console.CursorTop
162 |
163 | match handler() with
164 | | NavigateBack ->
165 | (* If the user does some prints we have to delete those lines as well *)
166 | let cursorDiff = Console.CursorTop-cursorPosBeforeExecute
167 | clear (subMenu.Length+cursorDiff)
168 |
169 | let (previousEntry, stack) =
170 | match callStack with
171 | | [] -> (menuEntry, callStack)
172 | | hd::tail -> (hd,tail)
173 |
174 | renderSubMenuRec stack previousEntry emphasize
175 |
176 | | Stay ->
177 | (* If the user does some prints we have to delete those lines as well *)
178 | let cursorDiff = Console.CursorTop-cursorPosBeforeExecute
179 | clear (subMenu.Length+cursorDiff)
180 | renderSubMenuRec callStack menuEntry emphasize
181 |
182 | | Exit -> ()
183 |
184 | | Sub sub ->
185 | clear subMenu.Length
186 | renderSubMenuRec (menuEntry::callStack) (snd subMenu.[currentEntry]) 0
187 |
188 | | ConsoleKey.Backspace ->
189 | match callStack with
190 | | [] ->
191 | handleUserInput currentEntry
192 |
193 | | hd::t -> // hd contains the previous
194 | clear subMenu.Length
195 | renderSubMenuRec t hd 0
196 |
197 | | _ -> handleUserInput currentEntry
198 |
199 | handleUserInput emphasize
200 |
201 | | _ -> ()
202 |
203 | renderSubMenuRec [] menuEntry 0
204 |
205 |
206 | let render menuEntry emphesizer =
207 | renderColored menuEntry emphesizer Color.None Colorize.Emphasizer
208 |
209 | let renderWithColoredEmphaziser menuEntry emphaziser color =
210 | renderColored menuEntry emphaziser color Colorize.Emphasizer
211 |
212 | let renderWithColoredEntry menuEntry color =
213 | renderColored menuEntry System.String.Empty color Colorize.Entry
214 |
215 | let renderWithColoredLine menuEntry emphesizer color =
216 | renderColored menuEntry emphesizer color Colorize.Both
217 |
--------------------------------------------------------------------------------
/src/FsMenu.Core/paket.references:
--------------------------------------------------------------------------------
1 | System.ValueTuple
--------------------------------------------------------------------------------
/tests/FsMenu.Console/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/tests/FsMenu.Console/FsMenu.Console.fsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | 2.0
8 | ea938605-9ecd-45e1-bfef-8f6b1f5aefa4
9 | Exe
10 | FsMenu
11 | FsMenu.Console
12 | v4.5.2
13 | true
14 | 4.4.1.0
15 | FsMenu.Console
16 |
17 |
18 | true
19 | full
20 | false
21 | false
22 | bin\$(Configuration)\
23 | DEBUG;TRACE
24 | 3
25 | AnyCPU
26 | bin\$(Configuration)\$(AssemblyName).XML
27 | true
28 |
29 |
30 | pdbonly
31 | true
32 | true
33 | bin\$(Configuration)\
34 | TRACE
35 | 3
36 | AnyCPU
37 | bin\$(Configuration)\$(AssemblyName).XML
38 | true
39 |
40 |
41 | 11
42 |
43 |
44 |
45 |
46 | $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets
47 |
48 |
49 |
50 |
51 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | AssemblyInfo.fs
61 |
62 |
63 |
64 |
65 |
66 |
67 | FSharp.Core
68 | FSharp.Core.dll
69 | $(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\$(TargetFSharpCoreVersion)\FSharp.Core.dll
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | FsMenu.Core
78 | {b1e153e6-8a13-4d07-81f5-161fd571c1be}
79 | True
80 |
81 |
82 |
89 |
90 |
91 |
92 |
93 |
94 | ..\..\packages\System.ValueTuple\lib\netstandard1.0\System.ValueTuple.dll
95 | True
96 | True
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/tests/FsMenu.Console/Program.fs:
--------------------------------------------------------------------------------
1 | open System
2 | open FsMenu.Core
3 | open FsMenu.Colors
4 |
5 |
6 | let testFunc() =
7 | printfn "selected Sub Sub 3"
8 | printf "handle some input: "
9 | let input = Console.ReadLine()
10 | // Do some stuff with input
11 | ()
12 |
13 |
14 | []
15 | let main argv =
16 |
17 | (*
18 | The ugly way
19 | don't use it
20 | *)
21 |
22 | let mutable yesOrNoUglyWay = ""
23 |
24 | let uglyTest =
25 | Sub ([
26 | ("Item 1", MenuEntry.Action (fun () -> printf "selected Item 1"; Exit))
27 | ("Item 2",
28 | Sub ([
29 | ("Sub 1",
30 | Sub ([
31 | ("Sub Sub 1", MenuEntry.Action (fun () -> printf "selected Sub Sub 1"; Exit))
32 | ("Sub Sub 2",
33 | Sub ([
34 | ("yes", Action (fun () -> yesOrNoUglyWay <- "--yes"; NavigateBack))
35 | ("no ", Action (fun () -> yesOrNoUglyWay <- "--no"; NavigateBack))
36 | ]))
37 | ("Sub Sub 3", MenuEntry.Action (fun () -> testFunc(); NavigateBack))
38 | ]))
39 | ("Sub 2", MenuEntry.Action (fun () -> printf "selected Sub 2"; Exit))
40 | ("Sub 3", MenuEntry.Action (fun () -> printf "exec some command with param %s" yesOrNoUglyWay; Exit))
41 | ]))
42 | ])
43 |
44 |
45 | printfn "Use Keys: UP-Arrow DOWN-Arrow ENTER BACK-SPACE\n"
46 |
47 | (*
48 | The same using a small DSL
49 | *)
50 |
51 | let mutable yesOrNo = ""
52 |
53 | let test =
54 | Menu [
55 | "Item 1" => (fun () -> printf "selected Item 1")
56 | "Item 2" +>
57 | Menu [
58 | "Sub 1" +>
59 | Menu [
60 | "Sub Sub 1" => (fun () -> printf "selected Sub Sub 1")
61 | "Sub Sub 2" +>
62 | Menu [
63 | "yes" <+ (fun () -> yesOrNo <- "--yes")
64 | "no " <+ (fun () -> yesOrNo <- "--no") ]
65 | "Sub Sub 3" <+ testFunc]
66 | "Sub 2" => (fun () -> printf "selected Sub 2")
67 | "Sub 3" => (fun () -> printf "exec some command with param %s" yesOrNo)]]
68 |
69 |
70 | //render test "<--"
71 | //renderWithColoredEmphaziser test "<--" Color.Green
72 | //renderWithColoredEntry test Color.Cyan
73 | renderWithColoredLine test "<--" Color.Green
74 |
75 |
76 | Console.ReadLine() |> ignore
77 | 0
78 |
--------------------------------------------------------------------------------
/tests/FsMenu.Console/paket.references:
--------------------------------------------------------------------------------
1 | System.ValueTuple
--------------------------------------------------------------------------------