{"id":6308,"date":"2024-07-11T15:19:29","date_gmt":"2024-07-11T07:19:29","guid":{"rendered":"https:\/\/t.n-years.com\/?p=6308"},"modified":"2024-07-26T17:33:00","modified_gmt":"2024-07-26T09:33:00","slug":"%e5%a6%82%e4%bd%95%e5%9c%a8-xcode-%e4%b8%ad%e5%ae%9e%e7%8e%b0-lsp-%e4%b8%ad%e7%9a%84-client","status":"publish","type":"post","link":"https:\/\/t.n-years.com\/?p=6308","title":{"rendered":"\u5982\u4f55\u5728 Xcode \u4e2d\u5b9e\u73b0 LSP \u4e2d\u7684 Client"},"content":{"rendered":"<p>\u975e\u539f\u521b\u3001ChatGPT \u95ee\u7b54\u751f\u6210<br \/>\n\u5728 Xcode \u4e2d\u5b9e\u73b0 LSP Client \u6d89\u53ca\u591a\u4e2a\u6b65\u9aa4\uff0c\u5305\u62ec\u8bbe\u7f6e Xcode \u9879\u76ee\u3001\u5b9e\u73b0 LSP \u901a\u4fe1\u3001\u5904\u7406 LSP \u8bf7\u6c42\u548c\u54cd\u5e94\u7b49\u3002\u4e0b\u9762\u662f\u4e00\u4e2a\u7b80\u5355\u7684\u6307\u5357\uff0c\u5e2e\u52a9\u4f60\u5728 Xcode \u4e2d\u5b9e\u73b0 LSP Client\u3002<\/p>\n<h3>1. \u8bbe\u7f6e Xcode \u9879\u76ee<\/h3>\n<p>\u9996\u5148\uff0c\u521b\u5efa\u4e00\u4e2a\u65b0\u7684 macOS \u5e94\u7528\u7a0b\u5e8f\u9879\u76ee\u3002\u5728 Xcode \u4e2d\uff0c\u9009\u62e9 <code>File<\/code> &gt; <code>New<\/code> &gt; <code>Project<\/code>\uff0c\u7136\u540e\u9009\u62e9 <code>macOS<\/code> \u5e73\u53f0\u5e76\u9009\u62e9 <code>App<\/code> \u6a21\u677f\u3002<\/p>\n<h3>2. \u6dfb\u52a0\u4f9d\u8d56\u5e93<\/h3>\n<p>\u4f60\u5c06\u9700\u8981\u4e00\u4e9b\u7b2c\u4e09\u65b9\u5e93\u6765\u5e2e\u52a9\u5904\u7406 JSON \u548c\u7f51\u7edc\u901a\u4fe1\u3002\u4f60\u53ef\u4ee5\u4f7f\u7528 Swift Package Manager (SPM) \u6dfb\u52a0\u8fd9\u4e9b\u4f9d\u8d56\u5e93\u3002<\/p>\n<ul>\n<li><code>SwiftJSON<\/code>\uff1a\u5904\u7406 JSON \u6570\u636e<\/li>\n<li><code>Starscream<\/code>\uff1aWebSocket \u5e93\uff0c\u7528\u4e8e\u4e0e LSP \u670d\u52a1\u5668\u8fdb\u884c\u901a\u4fe1<\/li>\n<\/ul>\n<p>\u5728\u4f60\u7684 <code>Package.swift<\/code> \u6587\u4ef6\u4e2d\u6dfb\u52a0\u4ee5\u4e0b\u5185\u5bb9\uff1a<\/p>\n<pre><code class=\"language-swift\">\/\/ swift-tools-version:5.3\nimport PackageDescription\n\nlet package = Package(\n    name: &quot;LSPClient&quot;,\n    dependencies: [\n        .package(url: &quot;https:\/\/github.com\/SwiftyJSON\/SwiftyJSON.git&quot;, from: &quot;5.0.0&quot;),\n        .package(url: &quot;https:\/\/github.com\/daltoniam\/Starscream.git&quot;, from: &quot;4.0.0&quot;),\n    ],\n    targets: [\n        .target(\n            name: &quot;LSPClient&quot;,\n            dependencies: [&quot;SwiftyJSON&quot;, &quot;Starscream&quot;]),\n        .testTarget(\n            name: &quot;LSPClientTests&quot;,\n            dependencies: [&quot;LSPClient&quot;]),\n    ]\n)<\/code><\/pre>\n<h3>3. \u5b9e\u73b0 LSP \u901a\u4fe1<\/h3>\n<p>\u5728\u9879\u76ee\u4e2d\u521b\u5efa\u4e00\u4e2a\u65b0\u7684 Swift \u6587\u4ef6\uff0c\u7528\u4e8e\u5b9e\u73b0\u4e0e LSP \u670d\u52a1\u5668\u7684\u901a\u4fe1\u3002<\/p>\n<h4>\u8fde\u63a5\u5230 LSP \u670d\u52a1\u5668<\/h4>\n<p>\u4f7f\u7528 <code>Starscream<\/code> \u8fde\u63a5\u5230 LSP \u670d\u52a1\u5668\uff1a<\/p>\n<pre><code class=\"language-swift\">import Foundation\nimport Starscream\nimport SwiftyJSON\n\nclass LSPClient: WebSocketDelegate {\n    var socket: WebSocket!\n\n    init() {\n        var request = URLRequest(url: URL(string: &quot;ws:\/\/localhost:8080&quot;)!)\n        request.timeoutInterval = 5\n        socket = WebSocket(request: request)\n        socket.delegate = self\n    }\n\n    func connect() {\n        socket.connect()\n    }\n\n    func websocketDidConnect(socket: WebSocketClient) {\n        print(&quot;Connected to LSP server&quot;)\n        sendInitializeRequest()\n    }\n\n    func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {\n        print(&quot;Disconnected from LSP server: \\(String(describing: error))&quot;)\n    }\n\n    func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {\n        print(&quot;Received message: \\(text)&quot;)\n        handleResponse(text: text)\n    }\n\n    func websocketDidReceiveData(socket: WebSocketClient, data: Data) {\n        print(&quot;Received data: \\(data.count) bytes&quot;)\n    }\n\n    func sendInitializeRequest() {\n        let params: [String: Any] = [\n            &quot;processId&quot;: ProcessInfo.processInfo.processIdentifier,\n            &quot;rootUri&quot;: &quot;file:\/\/\/\\(NSHomeDirectory())&quot;,\n            &quot;capabilities&quot;: [:]\n        ]\n        let message: [String: Any] = [\n            &quot;jsonrpc&quot;: &quot;2.0&quot;,\n            &quot;id&quot;: 1,\n            &quot;method&quot;: &quot;initialize&quot;,\n            &quot;params&quot;: params\n        ]\n        sendMessage(message: message)\n    }\n\n    func sendMessage(message: [String: Any]) {\n        if let jsonData = try? JSONSerialization.data(withJSONObject: message, options: []) {\n            let jsonString = String(data: jsonData, encoding: .utf8)!\n            socket.write(string: jsonString)\n        }\n    }\n\n    func handleResponse(text: String) {\n        if let data = text.data(using: .utf8) {\n            let json = try? JSON(data: data)\n            print(&quot;Response JSON: \\(String(describing: json))&quot;)\n        }\n    }\n}<\/code><\/pre>\n<h3>4. \u5904\u7406 LSP \u8bf7\u6c42\u548c\u54cd\u5e94<\/h3>\n<p>\u5728 <code>LSPClient<\/code> \u7c7b\u4e2d\u6dfb\u52a0\u5904\u7406 LSP \u8bf7\u6c42\u548c\u54cd\u5e94\u7684\u65b9\u6cd5\u3002\u4f8b\u5982\uff0c\u5904\u7406 <code>textDocument\/didOpen<\/code> \u548c <code>textDocument\/completion<\/code> \u8bf7\u6c42\u3002<\/p>\n<h4>\u5904\u7406 <code>textDocument\/didOpen<\/code> \u8bf7\u6c42<\/h4>\n<pre><code class=\"language-swift\">func sendDidOpenTextDocument(uri: String, languageId: String, version: Int, text: String) {\n    let params: [String: Any] = [\n        &quot;textDocument&quot;: [\n            &quot;uri&quot;: uri,\n            &quot;languageId&quot;: languageId,\n            &quot;version&quot;: version,\n            &quot;text&quot;: text\n        ]\n    ]\n    let message: [String: Any] = [\n        &quot;jsonrpc&quot;: &quot;2.0&quot;,\n        &quot;method&quot;: &quot;textDocument\/didOpen&quot;,\n        &quot;params&quot;: params\n    ]\n    sendMessage(message: message)\n}<\/code><\/pre>\n<h4>\u5904\u7406 <code>textDocument\/completion<\/code> \u8bf7\u6c42<\/h4>\n<pre><code class=\"language-swift\">func sendCompletionRequest(uri: String, position: [String: Int]) {\n    let params: [String: Any] = [\n        &quot;textDocument&quot;: [\n            &quot;uri&quot;: uri\n        ],\n        &quot;position&quot;: position\n    ]\n    let message: [String: Any] = [\n        &quot;jsonrpc&quot;: &quot;2.0&quot;,\n        &quot;id&quot;: 2,\n        &quot;method&quot;: &quot;textDocument\/completion&quot;,\n        &quot;params&quot;: params\n    ]\n    sendMessage(message: message)\n}<\/code><\/pre>\n<h3>5. \u6d4b\u8bd5 LSP Client<\/h3>\n<p>\u5728\u4f60\u7684\u5e94\u7528\u4e2d\u5b9e\u4f8b\u5316 <code>LSPClient<\/code> \u5e76\u6d4b\u8bd5\u5176\u529f\u80fd\uff1a<\/p>\n<pre><code class=\"language-swift\">import Cocoa\n\n@main\nclass AppDelegate: NSObject, NSApplicationDelegate {\n    var lspClient: LSPClient!\n\n    func applicationDidFinishLaunching(_ aNotification: Notification) {\n        lspClient = LSPClient()\n        lspClient.connect()\n\n        \/\/ \u793a\u4f8b\uff1a\u53d1\u9001\u6587\u672c\u6587\u6863\u6253\u5f00\u8bf7\u6c42\n        lspClient.sendDidOpenTextDocument(uri: &quot;file:\/\/\/path\/to\/file.swift&quot;, languageId: &quot;swift&quot;, version: 1, text: &quot;let a = 1&quot;)\n\n        \/\/ \u793a\u4f8b\uff1a\u53d1\u9001\u4ee3\u7801\u8865\u5168\u8bf7\u6c42\n        lspClient.sendCompletionRequest(uri: &quot;file:\/\/\/path\/to\/file.swift&quot;, position: [&quot;line&quot;: 0, &quot;character&quot;: 4])\n    }\n\n    func applicationWillTerminate(_ aNotification: Notification) {\n        \/\/ \u6e05\u7406\u8d44\u6e90\n    }\n}<\/code><\/pre>\n<p>\u4ee5\u4e0a\u6b65\u9aa4\u53ea\u662f\u4e00\u4e2a\u57fa\u672c\u793a\u4f8b\u3002\u6839\u636e\u4f60\u7684\u5177\u4f53\u9700\u6c42\uff0c\u4f60\u53ef\u80fd\u9700\u8981\u5904\u7406\u66f4\u591a\u7684 LSP \u65b9\u6cd5\u548c\u54cd\u5e94\uff0c\u5e76\u5b9e\u73b0\u66f4\u590d\u6742\u7684\u903b\u8f91\u3002\u901a\u8fc7\u8fd9\u4e9b\u6b65\u9aa4\uff0c\u4f60\u53ef\u4ee5\u5728 Xcode \u4e2d\u5b9e\u73b0\u4e00\u4e2a\u7b80\u5355\u7684 LSP Client\uff0c\u5e76\u4e0e LSP \u670d\u52a1\u5668\u8fdb\u884c\u901a\u4fe1\u3002<\/p>\n<p>MacOS \u4e0a\u7684\u5f00\u6e90\u5b9e\u73b0\uff1a<a href=\"https:\/\/github.com\/ChimeHQ\/LanguageClient\">https:\/\/github.com\/ChimeHQ\/LanguageClient<\/a><br \/>\n<a href=\"https:\/\/github.com\/ChimeHQ\/LanguageServerProtocol\">https:\/\/github.com\/ChimeHQ\/LanguageServerProtocol<\/a><br \/>\n<a href=\"https:\/\/medium.com\/@malintha1996\/understanding-the-language-server-protocol-5c0ba3ac83d2\">https:\/\/medium.com\/@malintha1996\/understanding-the-language-server-protocol-5c0ba3ac83d2<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u975e\u539f\u521b\u3001ChatGPT \u95ee\u7b54\u751f\u6210 \u5728 Xcode \u4e2d\u5b9e\u73b0 LSP Client \u6d89\u53ca\u591a\u4e2a\u6b65\u9aa4\uff0c\u5305\u62ec\u8bbe\u7f6e Xco&#8230; <\/p>\n<div class=\"read-more navbutton\"><a href=\"https:\/\/t.n-years.com\/?p=6308\">\u9605\u8bfb\u66f4\u591a<i class=\"fa fa-angle-double-right\"><\/i><\/a><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[119],"class_list":["post-6308","post","type-post","status-publish","format-standard","hentry","category-5","tag-lsp"],"_links":{"self":[{"href":"https:\/\/t.n-years.com\/index.php?rest_route=\/wp\/v2\/posts\/6308","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/t.n-years.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/t.n-years.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/t.n-years.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/t.n-years.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=6308"}],"version-history":[{"count":3,"href":"https:\/\/t.n-years.com\/index.php?rest_route=\/wp\/v2\/posts\/6308\/revisions"}],"predecessor-version":[{"id":6406,"href":"https:\/\/t.n-years.com\/index.php?rest_route=\/wp\/v2\/posts\/6308\/revisions\/6406"}],"wp:attachment":[{"href":"https:\/\/t.n-years.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=6308"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/t.n-years.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=6308"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/t.n-years.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=6308"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}