1 #Region "Microsoft.VisualBasic::3c699bc9019711f804c65a69292fd44d, Microsoft.VisualBasic.Core\CommandLine\Interpreters\View\ManualBuilder.vb"
2
3     ' Author:
4     
5     '       asuka (amethyst.asuka@gcmodeller.org)
6     '       xie (genetics@smrucc.org)
7     '       xieguigang (xie.guigang@live.com)
8     
9     ' Copyright (c) 2018 GPL3 Licensed
10     
11     
12     ' GNU GENERAL PUBLIC LICENSE (GPL3)
13     
14     
15     ' This program is free software: you can redistribute it and/or modify
16     ' it under the terms of the GNU General Public License as published by
17     ' the Free Software Foundation, either version 3 of the License, or
18     ' (at your option) any later version.
19     
20     ' This program is distributed in the hope that it will be useful,
21     ' but WITHOUT ANY WARRANTY; without even the implied warranty of
22     ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     ' GNU General Public License for more details.
24     
25     ' You should have received a copy of the GNU General Public License
26     ' along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29
30     ' /********************************************************************************/
31
32     ' Summaries:
33
34     '     Module ManualBuilder
35     
36     '         Function: APIPrototype, ExampleValue, GetFileExtensions, PrintHelp
37     
38     
39     ' /********************************************************************************/
40
41 #End Region
42
43 Imports System.Runtime.CompilerServices
44 Imports Microsoft.VisualBasic.ApplicationServices
45 Imports Microsoft.VisualBasic.ApplicationServices.Terminal
46 Imports Microsoft.VisualBasic.CommandLine.Reflection
47 Imports Microsoft.VisualBasic.CommandLine.Reflection.EntryPoints
48 Imports Microsoft.VisualBasic.Language
49 Imports Microsoft.VisualBasic.Linq
50 Imports Microsoft.VisualBasic.Text
51
52 Namespace CommandLine.ManView
53
54     ''' <summary>
55     ''' 用来生成帮助信息
56     ''' </summary>
57     Module ManualBuilder
58
59         ''' <summary>
60         ''' Prints the formatted help information on the console.
61         ''' (用于生成打印在终端上面的命令行帮助信息)
62         ''' </summary>
63         ''' <param name="api"></param>
64         ''' <returns></returns>
65         <Extension> Public Function PrintHelp(api As APIEntryPoint) As Integer
66             ' 因为在编写帮助信息的时候可能会有多行字符串,则在vb源代码里面会出现前导的空格,
67             ' 所以在这里需要将每一行的前导空格删除掉, 否则会破坏掉输出的文本对齐格式。
68             Dim infoLines = api.Info _
69                 .LineTokens _
70                 .Select(Function(s) s.Trim(" "c, ASCII.TAB)) _
71                 .JoinBy(vbCrLf) _
72                 .SplitParagraph(90) _
73                 .ToArray
74             Dim blank$
75
76             If infoLines.IsNullOrEmpty Then
77                 infoLines = {"Description not available..."}
78             End If
79
80             ' print API name and description
81             Call Console.WriteLine()
82             Call Console.WriteLine($"   '{api.Name}' - {infoLines.FirstOrDefault}")
83
84             If infoLines.Length > 1 Then
85                 blank = New String(
86                     " ",
87                     3 + ' 三个前导空格
88                     2 + ' 两个命令行名称左右的单引号
89                     3 + ' 空格-空格
90                     api.Name.Length)
91
92                 For Each line$ In infoLines.Skip(1)
93                     Call Console.WriteLine($"{blank}{line}")
94                 Next
95             End If
96
97             ' print usage
98             With Console.ForegroundColor
99
100                 Call Console.WriteLine()
101                 Call Console.WriteLine($"Usage:")
102                 Call Console.WriteLine()
103
104                 Console.ForegroundColor = ConsoleColor.Cyan
105
106                 Call Console.Write("  ")
107                 Call Console.Write(
108                     If(App.Platform = PlatformID.Unix OrElse
109                     App.Platform = PlatformID.MacOSX,
110                     App.ExecutablePath.TrimSuffix,
111                     App.ExecutablePath) & " ")
112
113                 Console.ForegroundColor = ConsoleColor.Green
114                 Call Console.WriteLine(api.Usage)
115                 Console.ForegroundColor = .ByRef
116
117             End With
118
119             If Not api.Arguments.IsNullOrEmpty Then
120                 Call Console.WriteLine()
121                 Call Console.WriteLine("  Command with arguments:")
122                 Call Console.WriteLine("  ====================================================")
123                 Call Console.WriteLine()
124
125                 ' 先计算出可能的最长的前导字符串的组合
126                 Dim maxPrefix% = -999
127                 Dim s$
128                 Dim std_in As Boolean = False
129                 Dim std_out As Boolean = False
130                 Dim bool As Boolean = False
131                 Dim haveOptional As Boolean = False
132                 Dim boolSeperator As Boolean = False
133                 Dim stringL$()
134
135                 ' 先输出必须的参数
136                 ' 之后为可选参数,但是可选参数会分为下面的顺序输出
137                 ' 1. 文件
138                 ' 2. 字符串
139                 ' 3. 数值
140                 ' 4. 整数
141                 ' 5. 逻辑值
142                 stringL = api.Arguments _
143                     .Select(Function(arg)
144                                 With arg.Value
145
146                                     If .TokenType = CLITypes.Boolean Then
147                                         ' 逻辑值类型的只能够是可选类型
148                                         s = "(optional) (boolean)"
149                                         bool = True
150                                     Else
151
152                                         If .Pipeline = PipelineTypes.std_in Then
153                                             s = "(*std_in)"
154                                             std_in = True
155                                         ElseIf .Pipeline = PipelineTypes.std_out Then
156                                             s = "(*std_out)"
157                                             std_out = True
158                                         Else
159                                             s = ""
160                                         End If
161
162                                         If .Optional Then
163                                             s &= "(optional)"
164                                             haveOptional = True
165                                         End If
166                                     End If
167                                 End With
168
169                                 Return s
170                             End Function) _
171                     .ToArray
172
173                 ' println("\n%s", stringL.MaxLengthString)
174
175                 ' 计算出诸如像(optional) (*std_in) (*std_out) (optional) (boolean)这类开关类型前导的
176                 ' 最大长度
177                 maxPrefix = stringL.MaxLengthString.Length
178
179                 ' 这里计算出来的是name usage的最大长度
180                 stringL$ = api.Arguments _
181                     .Select(Function(x) x.Value.Example) _
182                     .ToArray
183
184                 ' println("\n%s", stringL.MaxLengthString)
185
186                 Dim l%
187                 Dim maxLen% = stringL _
188                     .MaxLengthString _
189                     .Length
190
191                 Call stringL.MaxLengthString.__DEBUG_ECHO
192
193                 ' 加上开关名字的最大长度就是前面的开关说明部分的最大字符串长度
194                 ' 后面的description帮助信息的偏移量都是依据这个值计算出来的
195                 Dim helpOffset% = maxPrefix + maxLen
196                 Dim skipOptionalLine As Boolean = False
197
198                 ' 必须的参数放在前面,可选的参数都是在后面的位置
199                 For Each param As Argument In api.Arguments.Select(Function(x) x.Value)
200
201                     If param.TokenType = CLITypes.Boolean AndAlso Not boolSeperator Then
202                         boolSeperator = True
203
204                         Call Console.WriteLine()
205                         Call Console.WriteLine("  Options:")
206                         Call Console.WriteLine()
207                     End If
208
209                     Dim l% 这个参数就是当前的这个命令的前半部的标识符部分的字符串长度
210                     ' helpOffset%的值减去当前的长度l,即可得到当前的命令的help info的
211                     ' 偏移量
212
213                     If param.[Optional] Then
214                         Dim fore = Console.ForegroundColor
215
216                         If Not skipOptionalLine Then
217                             skipOptionalLine = True
218                             Call Console.WriteLine()
219                         End If
220
221                         Call Console.Write("  (")
222                         Console.ForegroundColor = ConsoleColor.Green
223                         Call Console.Write("optional")
224                         Console.ForegroundColor = fore
225                         Call Console.Write(") ")
226
227                         s = param.Example
228                     Else
229                         s = param.Example
230                         Console.Write("  ")
231                     End If
232
233                     l = s.Length
234
235                     With param
236                         If .Pipeline = PipelineTypes.std_in Then
237                             s = "(*std_in)  " & s
238                         ElseIf .Pipeline = PipelineTypes.std_out Then
239                             s = "(*std_out) " & s
240                         ElseIf .TokenType = CLITypes.Boolean Then
241                             s = "(boolean)  " & s
242                         Else
243                             If Not .Optional Then
244                                 s = New String(" "c, maxPrefix + 1) & s
245                             End If
246                         End If
247                     End With
248
249                     Call Console.Write(s)
250
251                     If param.TokenType = CLITypes.Boolean Then
252                         l += 11
253                     ElseIf param.Pipeline = PipelineTypes.std_out Then
254                         l += 11
255                     End If
256
257                     ' 这里的blank调整的是命令开关名称与描述之间的字符间距
258                     blank = New String(" "c, helpOffset - l + 2)
259                     infoLines$ = param.Description _
260                         .LineTokens _
261                         .Select(Function(str) str.Trim(" "c, ASCII.TAB)) _
262                         .JoinBy(vbCrLf) _
263                         .SplitParagraph(120) _
264                         .ToArray
265
266                     Call Console.Write(blank)
267                     Call Console.WriteLine($"{infoLines.FirstOrDefault}")
268
269                     If infoLines.Length > 1 Then
270                         Dim d% = 0
271
272                         If param.Optional Then
273                             d = 13
274                         End If
275
276                         blank = New String(" "c, helpOffset + d + 2)
277
278                         For Each line In infoLines.Skip(1)
279                             Call Console.WriteLine(blank & line)
280                         Next
281                     End If
282                 Next
283
284                 If std_in OrElse std_out OrElse bool Then
285                     Call Console.WriteLine()
286                 End If
287
288                 If std_in Then
289                     If std_out Then
290                         Call Console.WriteLine("  *std_in:  " & PipelineTypes.std_in.Description)
291                     Else
292                         Call Console.WriteLine("  *std_in: " & PipelineTypes.std_in.Description)
293                     End If
294                 End If
295                 If std_out Then
296                     Call Console.WriteLine("  *std_out: " & PipelineTypes.std_out.Description)
297                 End If
298
299                 Dim allExts = api.Arguments _
300                     .Select(Function(arg) arg.Value.GetFileExtensions) _
301                     .IteratesALL _
302                     .Distinct _
303                     .OrderBy(Function(ext) ext) _
304                     .ToArray
305
306                 If allExts.Length > 0 Then
307                     Call Console.WriteLine()
308
309                     Dim allContentTypes = allExts _
310                         .Select(Function(ext) (ext:=ext, Type:=ext.GetMIMEDescrib)) _
311                         .ToArray
312                     Dim table$()() = allContentTypes _
313                         .Select(Function(content)
314                                     With content.Type
315                                         Return {"  " & content.ext & "  ", $"({ .MIMEType})  ", .Name}
316                                     End With
317                                 End Function) _
318                         .ToArray
319
320                     Call table.Print
321                 End If
322
323                 If bool Then
324                     Call Console.WriteLine()
325                     Call Console.WriteLine("  " & boolFlag)
326                 End If
327             End If
328
329             Return 0
330         End Function
331
332         <Extension>
333         Public Function GetFileExtensions(arg As Argument) As String()
334             If arg.TokenType = CLITypes.File AndAlso Not arg.Extensions.StringEmpty Then
335                 Dim extensions$() = arg _
336                     .Extensions _
337                     .Split(","c) _
338                     .Select(AddressOf Trim) _
339                     .Select(Function(s)
340                                 If InStr(s, "*.") = 1 Then
341                                     Return s
342                                 Else
343                                     Return $"*.{s}"
344                                 End If
345                             End Function) _
346                     .ToArray
347
348                 Return extensions
349             Else
350                 Return Nothing
351             End If
352         End Function
353
354         ''' <summary>
355         ''' (boolean flag does not require of argument value)
356         ''' </summary>
357         Public Const boolFlag$ = "(boolean flag does not require of argument value)"
358
359         <Extension>
360         Public Function ExampleValue(arg As Argument) As String
361             Dim example$
362
363             Select Case arg.TokenType
364
365                 Case CLITypes.Double
366                     example = "<float>"
367                 Case CLITypes.Integer
368                     example = "<int32>"
369                 Case CLITypes.String
370                     example = "<term_string>"
371                 Case CLITypes.File
372
373                     With arg.GetFileExtensions
374                         If .IsNullOrEmpty Then
375                             example = "<file/directory>"
376                         Else
377                             example = $"<file, { .JoinBy("")}>"
378                         End If
379                     End With
380
381                 Case Else
382                     example = "unknown"
383             End Select
384
385             Return example
386         End Function
387
388         Const CLI$ = "(Microsoft.VisualBasic.CommandLine.CommandLine)"
389         Const VBStyle_CLI = "(args As Microsoft.VisualBasic.CommandLine.CommandLine)"
390
391         <MethodImpl(MethodImplOptions.AggressiveInlining)>
392         Public Function APIPrototype(declare$) As String
393             Return [declare].Replace(CLI, VBStyle_CLI)
394         End Function
395     End Module
396 End Namespace