10
10
//
11
11
//===----------------------------------------------------------------------===//
12
12
13
+ import ArgumentParser
14
+ import Csourcekitd // Not needed here, but fixes debugging...
13
15
import Dispatch
14
16
import Foundation
15
17
import LanguageServerProtocol
@@ -18,134 +20,170 @@ import LSPLogging
18
20
import SKCore
19
21
import SKSupport
20
22
import SourceKitLSP
21
- import Csourcekitd // Not needed here, but fixes debugging...
22
23
import TSCBasic
23
24
import TSCLibc
24
- import TSCUtility
25
-
26
- extension LogLevel : ArgumentKind {
27
- public static var completion : ShellCompletion {
28
- return ShellCompletion . none
29
- }
30
- }
31
-
32
- struct CommandLineOptions {
33
- /// Options for the server.
34
- var serverOptions : SourceKitServer . Options = SourceKitServer . Options ( )
35
-
36
- /// Whether to wait for a response before handling the next request.
37
- var syncRequests : Bool = false
38
- }
39
25
40
- func parseArguments( ) throws -> CommandLineOptions {
41
- let arguments = Array ( ProcessInfo . processInfo. arguments. dropFirst ( ) )
42
- let parser = ArgumentParser ( usage: " [options] " , overview: " Language Server Protocol implementation for Swift and C-based languages " )
43
- let loggingOption = parser. add ( option: " --log-level " , kind: LogLevel . self, usage: " Set the logging level (debug|info|warning|error) [default: \( LogLevel . default) ] " )
44
- let syncOption = parser. add ( option: " --sync " , kind: Bool . self) // For testing.
45
- let buildConfigurationOption = parser. add ( option: " --configuration " , shortName: " -c " , kind: BuildConfiguration . self, usage: " Build with configuration (debug|release) [default: debug] " )
46
- let buildPathOption = parser. add ( option: " --build-path " , kind: PathArgument . self, usage: " Specify build/cache directory " )
47
- let buildFlagsCc = parser. add ( option: " -Xcc " , kind: [ String ] . self, strategy: . oneByOne, usage: " Pass flag through to all C compiler invocations " )
48
- let buildFlagsCxx = parser. add ( option: " -Xcxx " , kind: [ String ] . self, strategy: . oneByOne, usage: " Pass flag through to all C++ compiler invocations " )
49
- let buildFlagsLinker = parser. add ( option: " -Xlinker " , kind: [ String ] . self, strategy: . oneByOne, usage: " Pass flag through to all linker invocations " )
50
- let buildFlagsSwift = parser. add ( option: " -Xswiftc " , kind: [ String ] . self, strategy: . oneByOne, usage: " Pass flag through to all Swift compiler invocations " )
51
- let clangdOptions = parser. add ( option: " -Xclangd " , kind: [ String ] . self, strategy: . oneByOne, usage: " Pass options to clangd command-line " )
52
- let indexStorePath = parser. add ( option: " -index-store-path " , kind: PathArgument . self, usage: " Override index-store-path from the build system " )
53
- let indexDatabasePath = parser. add ( option: " -index-db-path " , kind: PathArgument . self, usage: " Override index-database-path from the build system " )
54
- let completionServerSideFiltering = parser. add ( option: " -completion-server-side-filtering " , kind: Bool . self, usage: " Whether to enable server-side filtering in code-completion " )
55
- let completionMaxResults = parser. add ( option: " -completion-max-results " , kind: Int . self, usage: " When server-side filtering is enabled, the maximum number of results to return " )
56
-
57
- let parsedArguments = try parser. parse ( arguments)
58
-
59
- var result = CommandLineOptions ( )
60
-
61
- if let config = parsedArguments. get ( buildConfigurationOption) {
62
- result. serverOptions. buildSetup. configuration = config
63
- }
64
- if let buildPath = parsedArguments. get ( buildPathOption) ? . path {
65
- result. serverOptions. buildSetup. path = buildPath
66
- }
67
- if let flags = parsedArguments. get ( buildFlagsCc) {
68
- result. serverOptions. buildSetup. flags. cCompilerFlags = flags
69
- }
70
- if let flags = parsedArguments. get ( buildFlagsCxx) {
71
- result. serverOptions. buildSetup. flags. cxxCompilerFlags = flags
72
- }
73
- if let flags = parsedArguments. get ( buildFlagsLinker) {
74
- result. serverOptions. buildSetup. flags. linkerFlags = flags
75
- }
76
- if let flags = parsedArguments. get ( buildFlagsSwift) {
77
- result. serverOptions. buildSetup. flags. swiftCompilerFlags = flags
26
+ extension AbsolutePath : ExpressibleByArgument {
27
+ public init ? ( argument: String ) {
28
+ if let cwd = localFileSystem. currentWorkingDirectory {
29
+ self . init ( argument, relativeTo: cwd)
30
+ } else {
31
+ guard let path = try ? AbsolutePath ( validating: argument) else {
32
+ return nil
33
+ }
34
+ self = path
35
+ }
78
36
}
79
37
80
- if let options = parsedArguments. get ( clangdOptions) {
81
- result. serverOptions. clangdOptions = options
38
+ public static var defaultCompletionKind : CompletionKind {
39
+ // This type is most commonly used to select a directory, not a file.
40
+ // Specify '.file()' in an argument declaration when necessary.
41
+ . directory
82
42
}
43
+ }
83
44
84
- if let path = parsedArguments. get ( indexStorePath) ? . path {
85
- result. serverOptions. indexOptions. indexStorePath = path
86
- }
87
- if let path = parsedArguments. get ( indexDatabasePath) ? . path {
88
- result. serverOptions. indexOptions. indexDatabasePath = path
89
- }
45
+ extension LogLevel : ExpressibleByArgument { }
46
+ extension BuildConfiguration : ExpressibleByArgument { }
90
47
91
- if let logLevel = parsedArguments. get ( loggingOption) {
92
- Logger . shared. currentLevel = logLevel
93
- } else {
94
- Logger . shared. setLogLevel ( environmentVariable: " SOURCEKIT_LOGGING " )
95
- }
48
+ struct Main : ParsableCommand {
49
+ static let configuration = CommandConfiguration (
50
+ abstract: " Language Server Protocol implementation for Swift and C-based languages "
51
+ )
96
52
97
- if let sync = parsedArguments. get ( syncOption) {
98
- result. syncRequests = sync
53
+ /// Whether to wait for a response before handling the next request.
54
+ /// Used for testing.
55
+ @Flag ( name: . customLong( " sync " ) )
56
+ var syncRequests = false
57
+
58
+ @Option ( help: " Set the logging level [debug|info|warning|error] (default: \( LogLevel . default) ) " )
59
+ var logLevel : LogLevel ?
60
+
61
+ @Option (
62
+ name: [ . customLong( " configuration " ) , . customShort( " c " ) ] ,
63
+ help: " Build with configuration [debug|release] "
64
+ )
65
+ var buildConfiguration = BuildConfiguration . debug
66
+
67
+ @Option ( help: " Specify build/cache directory " )
68
+ var buildPath : AbsolutePath ?
69
+
70
+ @Option (
71
+ name: . customLong( " Xcc " , withSingleDash: true ) ,
72
+ parsing: . unconditionalSingleValue,
73
+ help: " Pass flag through to all C compiler invocations "
74
+ )
75
+ var buildFlagsCc = [ String] ( )
76
+
77
+ @Option (
78
+ name: . customLong( " Xcxx " , withSingleDash: true ) ,
79
+ parsing: . unconditionalSingleValue,
80
+ help: " Pass flag through to all C++ compiler invocations "
81
+ )
82
+ var buildFlagsCxx = [ String] ( )
83
+
84
+ @Option (
85
+ name: . customLong( " Xlinker " , withSingleDash: true ) ,
86
+ parsing: . unconditionalSingleValue,
87
+ help: " Pass flag through to all linker invocations "
88
+ )
89
+ var buildFlagsLinker = [ String] ( )
90
+
91
+ @Option (
92
+ name: . customLong( " Xswiftc " , withSingleDash: true ) ,
93
+ parsing: . unconditionalSingleValue,
94
+ help: " Pass flag through to all Swift compiler invocations "
95
+ )
96
+ var buildFlagsSwift = [ String] ( )
97
+
98
+ @Option (
99
+ name: . customLong( " Xclangd " , withSingleDash: true ) ,
100
+ parsing: . unconditionalSingleValue,
101
+ help: " Pass options to clangd command-line "
102
+ )
103
+ var clangdOptions = [ String] ( )
104
+
105
+ @Option (
106
+ name: . customLong( " index-store-path " , withSingleDash: true ) ,
107
+ help: " Override index-store-path from the build system "
108
+ )
109
+ var indexStorePath : AbsolutePath ?
110
+
111
+ @Option (
112
+ name: . customLong( " index-db-path " , withSingleDash: true ) ,
113
+ help: " Override index-database-path from the build system "
114
+ )
115
+ var indexDatabasePath : AbsolutePath ?
116
+
117
+ @Option (
118
+ help: " Whether to enable server-side filtering in code-completion "
119
+ )
120
+ var completionServerSideFiltering = true
121
+
122
+ @Option (
123
+ help: " When server-side filtering is enabled, the maximum number of results to return "
124
+ )
125
+ var completionMaxResults = 200
126
+
127
+ func mapOptions( ) -> SourceKitServer . Options {
128
+ var serverOptions = SourceKitServer . Options ( )
129
+
130
+ serverOptions. buildSetup. configuration = buildConfiguration
131
+ serverOptions. buildSetup. path = buildPath
132
+ serverOptions. buildSetup. flags. cCompilerFlags = buildFlagsCc
133
+ serverOptions. buildSetup. flags. cxxCompilerFlags = buildFlagsCxx
134
+ serverOptions. buildSetup. flags. linkerFlags = buildFlagsLinker
135
+ serverOptions. buildSetup. flags. swiftCompilerFlags = buildFlagsSwift
136
+ serverOptions. clangdOptions = clangdOptions
137
+ serverOptions. indexOptions. indexStorePath = indexStorePath
138
+ serverOptions. indexOptions. indexDatabasePath = indexDatabasePath
139
+ serverOptions. completionOptions. serverSideFiltering = completionServerSideFiltering
140
+ serverOptions. completionOptions. maxResults = completionMaxResults
141
+
142
+ return serverOptions
99
143
}
100
144
101
- if let serverSideFiltering = parsedArguments. get ( completionServerSideFiltering) {
102
- result. serverOptions. completionOptions. serverSideFiltering = serverSideFiltering
103
- }
104
- if let maxResults = parsedArguments. get ( completionMaxResults) {
105
- result. serverOptions. completionOptions. maxResults = maxResults
145
+ func run( ) throws {
146
+ if let logLevel = logLevel {
147
+ Logger . shared. currentLevel = logLevel
148
+ } else {
149
+ Logger . shared. setLogLevel ( environmentVariable: " SOURCEKIT_LOGGING " )
150
+ }
151
+
152
+ // Dup stdout and redirect the fd to stderr so that a careless print()
153
+ // will not break our connection stream.
154
+ let realStdout = dup ( STDOUT_FILENO)
155
+ if realStdout == - 1 {
156
+ fatalError ( " failed to dup stdout: \( strerror ( errno) !) " )
157
+ }
158
+ if dup2 ( STDERR_FILENO, STDOUT_FILENO) == - 1 {
159
+ fatalError ( " failed to redirect stdout -> stderr: \( strerror ( errno) !) " )
160
+ }
161
+
162
+ let clientConnection = JSONRPCConnection (
163
+ protocol: MessageRegistry . lspProtocol,
164
+ inFD: STDIN_FILENO,
165
+ outFD: realStdout,
166
+ syncRequests: syncRequests
167
+ )
168
+
169
+ let installPath = AbsolutePath ( Bundle . main. bundlePath)
170
+ ToolchainRegistry . shared = ToolchainRegistry ( installPath: installPath, localFileSystem)
171
+
172
+ let server = SourceKitServer ( client: clientConnection, options: mapOptions ( ) , onExit: {
173
+ clientConnection. close ( )
174
+ } )
175
+ clientConnection. start ( receiveHandler: server, closeHandler: {
176
+ server. prepareForExit ( )
177
+ // Use _Exit to avoid running static destructors due to SR-12668.
178
+ _Exit ( 0 )
179
+ } )
180
+
181
+ Logger . shared. addLogHandler { message, _ in
182
+ clientConnection. send ( LogMessageNotification ( type: . log, message: message) )
183
+ }
184
+
185
+ dispatchMain ( )
106
186
}
107
-
108
- return result
109
- }
110
-
111
- let options : CommandLineOptions
112
- do {
113
- options = try parseArguments ( )
114
- } catch {
115
- fputs ( " error: \( error) \n " , TSCLibc . stderr)
116
- exit ( 1 )
117
- }
118
-
119
- // Dup stdout and redirect the fd to stderr so that a careless print()
120
- // will not break our connection stream.
121
- let realStdout = dup ( STDOUT_FILENO)
122
- if realStdout == - 1 {
123
- fatalError ( " failed to dup stdout: \( strerror ( errno) !) " )
124
- }
125
- if dup2 ( STDERR_FILENO, STDOUT_FILENO) == - 1 {
126
- fatalError ( " failed to redirect stdout -> stderr: \( strerror ( errno) !) " )
127
- }
128
-
129
- let clientConnection = JSONRPCConnection (
130
- protocol: MessageRegistry . lspProtocol,
131
- inFD: STDIN_FILENO,
132
- outFD: realStdout,
133
- syncRequests: options. syncRequests)
134
-
135
- let installPath = AbsolutePath ( Bundle . main. bundlePath)
136
- ToolchainRegistry . shared = ToolchainRegistry ( installPath: installPath, localFileSystem)
137
-
138
- let server = SourceKitServer ( client: clientConnection, options: options. serverOptions, onExit: {
139
- clientConnection. close ( )
140
- } )
141
- clientConnection. start ( receiveHandler: server, closeHandler: {
142
- server. prepareForExit ( )
143
- // Use _Exit to avoid running static destructors due to SR-12668.
144
- _Exit ( 0 )
145
- } )
146
-
147
- Logger . shared. addLogHandler { message, _ in
148
- clientConnection. send ( LogMessageNotification ( type: . log, message: message) )
149
187
}
150
188
151
- dispatchMain ( )
189
+ Main . main ( )
0 commit comments