{"id":17150784,"url":"https://github.com/object88/langd","last_synced_at":"2025-06-25T10:32:25.467Z","repository":{"id":57540504,"uuid":"102417642","full_name":"object88/langd","owner":"object88","description":"A Language Server Protocol implementation in Go for Go","archived":false,"fork":false,"pushed_at":"2018-12-25T23:03:17.000Z","size":22144,"stargazers_count":7,"open_issues_count":15,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-27T03:04:29.977Z","etag":null,"topics":["go","language-server"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/object88.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-09-05T01:16:26.000Z","updated_at":"2024-08-03T23:51:09.000Z","dependencies_parsed_at":"2022-09-26T18:30:37.281Z","dependency_job_id":null,"html_url":"https://github.com/object88/langd","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/object88%2Flangd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/object88%2Flangd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/object88%2Flangd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/object88%2Flangd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/object88","download_url":"https://codeload.github.com/object88/langd/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248710439,"owners_count":21149188,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","language-server"],"created_at":"2024-10-14T21:36:30.066Z","updated_at":"2025-04-13T11:52:53.092Z","avatar_url":"https://github.com/object88.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# langd\n\n## Notes\n\n### Server behavior\n\nWhen scanning the root URI for folders with Go code, we are skipping:\n\n* any directory that begins with \".\" (i.e., `.git`, `.vscode`).\n* any directory that is symlinked.  See [filepath.Walk](https://golang.org/pkg/path/filepath/#Walk) description.\n\n### gRPC and Proto\n\nIf the gRPC API is changed (specifically, `/proto/langd.proto`), use the following command to rebuild the generated files.\n\n`protoc -I proto proto/langd.proto --go_out=plugins=grpc:proto`\n\n### Initialization\n\nNoting [the LSP spec for initialization](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#initialize-request), `initialize` should be the first request on a connection.  In this implementation, this request will be responded to before workspace initialization is complete.  The initialize responce will announce what capabilities the server has, which should include the `openClose` option.  If this workspace has been opened before, some files may automatically open, and the server may receive a `didOpen` request before initialiation is completed.\n\nConnection \u0026 handler initialization has three stages: `uninitialized`, `initializing`, and `initialized`.  Expectations for handling requests are detailed in the [Initialize Request](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#initialize-request) documentation in the LSP spec.\n\nIn the uninitialized state...\n\n``` text\n* for a request the respond should be errored with code: -32002. The message can be picked by the server.\n* notifications should be dropped, except for the exit notification. This will allow the exit a server without an initialize request.\n```\n\nAfter the client has sent an `initialize` request, the client is expected to not send any further requests until it receives the `InitializeResult` response.\n\nThe server may return an `InitializeResult` response before it is ready to process requests.  This is the `initializing` state.  During this time, the client may send requests, and the server must queue them up for processing.  The queue is _not_ being processed at this time.\n\nOnce internal initialization is complete, the server is in the `initialized` stage, and will begin processing the queue.  New requests are still queued up, but the server is free to process them.\n\n### Loading the workspace\n\nThe `initialize` request points to a filesystem path, which will be the root of the workspace.  That path and every directory under it will be scanned for Go code.  Additionally, and imported packages are loaded, down to the standard library.\n\nDuring the loading process, the loader engine keeps a map of discovered distinct packages; a distinct package is a set of package files differentiated by OS, architecture, or other build flags.  Each time that a distinct package is encountered, the map be checked, and if the entry is missing, a goroutine will be started to load the AST.\n\nAs each package is loaded (and all dependencies are loaded), that package is fed to `config.Check` to build the map of uses, definitions, etc.\n\n### Processing requests\n\nIncoming requests are asynchronously processed by a connection handler.  A connection handler has two queues: `incomingQueue` and `outgoingQueue`. As requests are received from the JSONRPC2 server, they are handed off to the connection handler, which looks up and instantiates a request handler by method name. The request handler immediately performs some preprocessing on the request to unmarshal arguments and perform any other setup. Once the preprocessing is complete, the request handler is placed on the `incomingQueue`.\n\nThe `incomingQueue` and `outgoingQueue` are processed in a GoRoutine. When a request handler is pulled off the `incomingQueue`, the `work` method is invoked, which is expected to perform the processing of the actual request. Once this is complete, the request handler is checked to see if it is also a reply handler, and if so, the request / reply handler is placed on the `outgoingQueue`. Notifications do not reply, so those requests are not placed on the `outgoingQueue`.\n\nReplies are supposed to be sent in same order as the requests. However, if the requests are processed asynchronously and some are faster to complete than others, then there is potenial for out-of-order replies. Additionally, some requests may require some inherent synchronous processing. For example, if the client sends a sequence of `didChange` notifications, those will need to be processed in order, and before a `definition` request is processed (as the `didChange` may have some bearing on the request of a definition).  For this reason, we use the Sigqueue struct to control the order of operations.\n\nTODO: Describe `Sigqueue` in greater detail, as it applies here\n\nThe connection handler may need a RWMutex to handle requests. Requests which do not alter state (`textDocument/definition`, `textDocument/references`, etc) enter with a Read lock, allowing any other non-altering requests to enter as well. Once a request which would alter state is processed (`textDocument/didChange`, `textDocument/rename`, etc), a Write lock is requested. All currently running read operations will need to complete before the write can proceed, and each write operation will need to proceed synchronously. (Conceivably, some write operations could be performed asynchronously, but that is out of scope for an initial implementation.)\n\nBecause replies may be generated out of order with asynchronous processing, they must be queued up in the `outgoingQueue`.\n\n### Changes to files\n\nWhen the IDE opens a file for the user, a `textDocument/didOpen` request is sent to the server.  The server creates a `rope` representation of the file contents, which is altered with `textDocument/didChange` requests.\n\n## Extensions to the Language Server Protocol\n\n### Server Health request\n\nThe client may request some basic health metrics from the server; including instanteous CPU and memory usage.\n\n_Request_\n\n* method: 'health/instant'\n* params: none\n\n_Response_\n\n* result: Instant defined as follows:\n\n``` typescript\ninterface HealthInformation {\n\t/**\n\t *\n\t */\n\tcpu: number;\n\n\t/**\n\t *\n\t */\n\tmemory: number;\n}\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobject88%2Flangd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fobject88%2Flangd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobject88%2Flangd/lists"}