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