diff --git a/Cargo.lock b/Cargo.lock index ea4e5b4..b36a42b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,72 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "itoa" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + [[package]] name = "lpp-conformance-runner" version = "0.1.0" @@ -22,6 +82,7 @@ version = "0.1.0" dependencies = [ "serde", "serde_json", + "sha2", ] [[package]] @@ -91,6 +152,17 @@ dependencies = [ "zmij", ] +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "syn" version = "3.0.3" @@ -102,12 +174,24 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "zmij" version = "1.0.23" diff --git a/README.md b/README.md index 31fbda5..5ce2156 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ its frontend, compiler, reconstruction, or Workshop integration internally. ## Status -- Protocol versions: **1.0**, additive **1.1** file-entry loading, and additive **1.2** directory targets (specified in [`spec/lpp-v1.md`](spec/lpp-v1.md)). +- Protocol versions: **1.0**, additive **1.1** file-entry loading, additive **1.2** directory targets, and additive **1.3** source identity for entry-based compilation (specified in [`spec/lpp-v1.md`](spec/lpp-v1.md)). - Repository state: initial published contract and conformance suite. - Wright is a client/consumer of the protocol; LPP is not a dependency from the language implementation back into Wright tooling internals. diff --git a/conformance/README.md b/conformance/README.md index df4d519..1674e69 100644 --- a/conformance/README.md +++ b/conformance/README.md @@ -1,6 +1,6 @@ # LPP v1 Conformance Suite -This directory contains the test suite and fixtures for the Language Provider Protocol v1 wire contract, including the LPP 1.1 file-entry and LPP 1.2 directory-target project-loading revisions (see [`../spec/lpp-v1.md`](../spec/lpp-v1.md)). +This directory contains the test suite and fixtures for the Language Provider Protocol v1 wire contract, including the LPP 1.1 file-entry, LPP 1.2 directory-target project-loading, and LPP 1.3 source-identity revisions (see [`../spec/lpp-v1.md`](../spec/lpp-v1.md)). ## Layout @@ -10,7 +10,7 @@ mock-provider/ Reference provider for the "x-demo-lang" equation DSL (Rus runner/ Conformance runner that replays fixtures against any provider binary ``` -* **Fixtures** (`fixtures/v1/`): one JSON file per scenario. Each scenario defines a session with request/response steps, optional CLI flags, and the expected exit code. Responses are compared after JSON parsing so key order does not matter. The directory contains LPP 1.0, LPP 1.1, and LPP 1.2 scenarios. +* **Fixtures** (`fixtures/v1/`): one JSON file per scenario. Each scenario defines a session with request/response steps, optional CLI flags, and the expected exit code. Responses are compared after JSON parsing so key order does not matter. The directory contains LPP 1.0, LPP 1.1, LPP 1.2, and LPP 1.3 scenarios. * **Mock provider** (`mock-provider/`): a small Rust binary implementing the full LPP v1 surface for a demonstration language distinct from OPY and OSTW. It runs over stdio so clients (like the Wright LPP client in wrightkit/wright#142) can test against it directly. * **Runner** (`runner/`): spawns a fresh provider process per scenario, feeds requests over stdin, validates stdout responses against expectations, and checks the process exit code. diff --git a/conformance/fixtures/v1/43-project-loading-source-identity.json b/conformance/fixtures/v1/43-project-loading-source-identity.json new file mode 100644 index 0000000..b8443d5 --- /dev/null +++ b/conformance/fixtures/v1/43-project-loading-source-identity.json @@ -0,0 +1,16 @@ +{ + "name": "project-loading-source-identity", + "description": "LPP 1.3 advertises source identity and returns it for an owner-selected directory compile target.", + "scope": "semantics", + "providerArgs": ["--protocol-version", "1.3"], + "projectFiles": { + "entry.xdl": "puzzle entry {\n target = 2\n start = 1\n ops {\n plus1: x => x + 1\n }\n solution = [ plus1 ]\n}\n", + "support.xdl": "puzzle support {\n target = 2\n start = 1\n ops {\n plus1: x => x + 1\n }\n solution = [ plus1 ]\n}\n" + }, + "steps": [ + {"request":{"jsonrpc":"2.0","id":1,"method":"lpp/initialize","params":{"protocolVersion":"1.3"}},"expectResponse":{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"1.3","serverInfo":{"name":"lpp-mock-provider","version":"0.1.0"},"languages":[{"id":"x-demo-lang","extensions":["xdl"]}],"capabilities":{"check":true,"compile":true,"reconstruct":true,"symbols":true,"definition":true,"references":true,"rename":true,"editValidation":true,"projectLoading":true,"sourceIdentity":true}}}}, + {"request":{"jsonrpc":"2.0","id":2,"method":"lpp/compile","params":{"entry":{"uri":"${PROJECT_URI}","languageId":"x-demo-lang","version":7,"kind":"directory"}}},"expectResponse":{"jsonrpc":"2.0","id":2,"result":{"diagnostics":[{"uri":"${PROJECT_URI}/entry.xdl","version":7,"diagnostics":[]},{"uri":"${PROJECT_URI}/support.xdl","version":7,"diagnostics":[]}],"sourceIdentity":"a92a4daf6631cbb574095cef0296d3d8217459a48e7899f98d832bb63f510822","artifact":{"format":"x-demo/puzzle-eval-v1","content":"{\"name\":\"entry\",\"ops\":[{\"arg\":1,\"name\":\"plus1\",\"op\":\"+\"}],\"solution\":[\"plus1\"],\"start\":1,\"target\":2,\"value\":2}"}}}}, + {"request":{"jsonrpc":"2.0","id":3,"method":"lpp/shutdown","params":{}},"expectResponse":{"jsonrpc":"2.0","id":3,"result":null}} + ], + "expectExitCode": 0 +} diff --git a/conformance/fixtures/v1/44-project-loading-source-identity-unavailable.json b/conformance/fixtures/v1/44-project-loading-source-identity-unavailable.json new file mode 100644 index 0000000..4616e53 --- /dev/null +++ b/conformance/fixtures/v1/44-project-loading-source-identity-unavailable.json @@ -0,0 +1,16 @@ +{ + "name": "project-loading-source-identity-unavailable", + "description": "An LPP 1.3 provider may decline source identity independently; entry compilation remains available without the optional field.", + "scope": "protocol", + "providerArgs": ["--protocol-version", "1.3", "--without", "sourceIdentity"], + "projectFiles": { + "entry.xdl": "puzzle entry {\n target = 2\n start = 1\n ops {\n plus1: x => x + 1\n }\n solution = [ plus1 ]\n}\n", + "support.xdl": "puzzle support {\n target = 2\n start = 1\n ops {\n plus1: x => x + 1\n }\n solution = [ plus1 ]\n}\n" + }, + "steps": [ + {"request":{"jsonrpc":"2.0","id":1,"method":"lpp/initialize","params":{"protocolVersion":"1.3"}},"expectResponse":{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"1.3","serverInfo":{"name":"lpp-mock-provider","version":"0.1.0"},"languages":[{"id":"x-demo-lang","extensions":["xdl"]}],"capabilities":{"check":true,"compile":true,"reconstruct":true,"symbols":true,"definition":true,"references":true,"rename":true,"editValidation":true,"projectLoading":true,"sourceIdentity":false}}}}, + {"request":{"jsonrpc":"2.0","id":2,"method":"lpp/compile","params":{"entry":{"uri":"${PROJECT_URI}","languageId":"x-demo-lang","version":7,"kind":"directory"}}},"expectResponse":{"jsonrpc":"2.0","id":2,"result":{"diagnostics":[{"uri":"${PROJECT_URI}/entry.xdl","version":7,"diagnostics":[]},{"uri":"${PROJECT_URI}/support.xdl","version":7,"diagnostics":[]}],"artifact":{"format":"x-demo/puzzle-eval-v1","content":"{\"name\":\"entry\",\"ops\":[{\"arg\":1,\"name\":\"plus1\",\"op\":\"+\"}],\"solution\":[\"plus1\"],\"start\":1,\"target\":2,\"value\":2}"}}}}, + {"request":{"jsonrpc":"2.0","id":3,"method":"lpp/shutdown","params":{}},"expectResponse":{"jsonrpc":"2.0","id":3,"result":null}} + ], + "expectExitCode": 0 +} diff --git a/conformance/mock-provider/Cargo.toml b/conformance/mock-provider/Cargo.toml index b8ae3df..ec83e5c 100644 --- a/conformance/mock-provider/Cargo.toml +++ b/conformance/mock-provider/Cargo.toml @@ -8,6 +8,7 @@ description = "LPP v1 conformance mock provider for the x-demo-lang puzzle/equat publish = false [dependencies] +sha2 = "0.10" serde = { workspace = true, features = ["derive"] } serde_json.workspace = true diff --git a/conformance/mock-provider/src/main.rs b/conformance/mock-provider/src/main.rs index ad39846..939e5e2 100644 --- a/conformance/mock-provider/src/main.rs +++ b/conformance/mock-provider/src/main.rs @@ -6,6 +6,7 @@ use std::path::{Path, PathBuf}; use serde::Deserialize; use serde_json::{Value, json}; +use sha2::{Digest, Sha256}; use puzzle::{ ARTIFACT_FORMAT, KIND_OP, KIND_PUZZLE, ParseOutput, Range, SourceText, compile_artifact, @@ -15,6 +16,7 @@ use puzzle::{ const DEFAULT_PROTOCOL_VERSION: &str = "1.0"; const PROTOCOL_VERSION_1_1: &str = "1.1"; const PROTOCOL_VERSION_1_2: &str = "1.2"; +const PROTOCOL_VERSION_1_3: &str = "1.3"; const SERVER_NAME: &str = "lpp-mock-provider"; const LANGUAGE_ID: &str = "x-demo-lang"; const LANGUAGE_EXTENSIONS: [&str; 1] = ["xdl"]; @@ -30,6 +32,7 @@ struct Capabilities { rename: bool, edit_validation: bool, project_loading: bool, + source_identity: bool, } impl Capabilities { @@ -44,6 +47,7 @@ impl Capabilities { rename: true, edit_validation: true, project_loading: true, + source_identity: true, } } @@ -58,6 +62,7 @@ impl Capabilities { "rename" => &mut self.rename, "editValidation" => &mut self.edit_validation, "projectLoading" => &mut self.project_loading, + "sourceIdentity" => &mut self.source_identity, _ => return false, }; *field = false; @@ -75,6 +80,7 @@ impl Capabilities { "rename" => self.rename, "editValidation" => self.edit_validation, "projectLoading" => self.project_loading, + "sourceIdentity" => self.source_identity, _ => false, } } @@ -92,10 +98,13 @@ impl Capabilities { }); if matches!( protocol_version, - PROTOCOL_VERSION_1_1 | PROTOCOL_VERSION_1_2 + PROTOCOL_VERSION_1_1 | PROTOCOL_VERSION_1_2 | PROTOCOL_VERSION_1_3 ) { capabilities["projectLoading"] = json!(self.project_loading); } + if protocol_version == PROTOCOL_VERSION_1_3 { + capabilities["sourceIdentity"] = json!(self.source_identity); + } capabilities } } @@ -290,6 +299,7 @@ fn parse_args() -> (String, Capabilities) { if version != DEFAULT_PROTOCOL_VERSION && version != PROTOCOL_VERSION_1_1 && version != PROTOCOL_VERSION_1_2 + && version != PROTOCOL_VERSION_1_3 { eprintln!("lpp-mock-provider: unsupported protocol version '{version}'"); std::process::exit(2); @@ -488,7 +498,7 @@ impl Server { )); } let doc = match entry_uri { - Some(uri) => documents_set.get(&uri).expect("loaded entry is present"), + Some(ref uri) => documents_set.get(uri).expect("loaded entry is present"), None => documents_set.values().next().expect("len == 1"), }; check_document(doc)?; @@ -515,10 +525,23 @@ impl Server { .expect("artifact serializes"); json!({ "format": ARTIFACT_FORMAT, "content": content }) }; - Ok(json!({ + let source_identity = entry_uri.as_ref().map(|uri| { + let mut hasher = Sha256::new(); + hasher.update(documents_set[uri].text.as_bytes()); + format!("{:x}", hasher.finalize()) + }); + let mut result = json!({ "diagnostics": diagnostics, "artifact": artifact, - })) + }); + if self.protocol_version.as_deref() == Some(PROTOCOL_VERSION_1_3) + && self.caps.source_identity + { + if let Some(source_identity) = source_identity { + result["sourceIdentity"] = Value::String(source_identity); + } + } + Ok(result) } fn reconstruct(&self, params: Value) -> Result { @@ -854,7 +877,9 @@ fn documents_for_request( (None, Some(entry)) => { if !matches!( server.protocol_version.as_deref(), - Some(PROTOCOL_VERSION_1_1) | Some(PROTOCOL_VERSION_1_2) + Some(PROTOCOL_VERSION_1_1) + | Some(PROTOCOL_VERSION_1_2) + | Some(PROTOCOL_VERSION_1_3) ) { return Err(HandlerError::Std(-32602, "Invalid params")); } @@ -887,7 +912,12 @@ fn project_target_kind( ) -> Result { match entry.kind.as_deref() { None | Some("file") => Ok(ProjectTargetKind::File), - Some("directory") if protocol_version == PROTOCOL_VERSION_1_2 => { + Some("directory") + if matches!( + protocol_version, + PROTOCOL_VERSION_1_2 | PROTOCOL_VERSION_1_3 + ) => + { Ok(ProjectTargetKind::Directory) } Some("directory") => Err(HandlerError::Lpp( diff --git a/spec/lpp-v1.md b/spec/lpp-v1.md index 3f5e2b9..202b5db 100644 --- a/spec/lpp-v1.md +++ b/spec/lpp-v1.md @@ -2,7 +2,7 @@ | | | | --- | --- | -| Protocol versions | `1.0`, `1.1`, `1.2` | +| Protocol versions | `1.0`, `1.1`, `1.2`, `1.3` | | Status | Normative for protocol major version 1 | | Transport | JSON-RPC 2.0 over stdio, newline-delimited framing | | Conformance | `conformance/fixtures/v1/` + `conformance/runner` + `conformance/mock-provider` | @@ -321,8 +321,8 @@ provider-owned filesystem project load. The example below selects a file: filesystem snapshot for this request. It is echoed in every source result. It is not a filesystem content hash and does not provide cross-request stale detection. -* `kind`: OPTIONAL for LPP 1.1 and LPP 1.2. When omitted, it requests the - existing file-entry behavior. LPP 1.2 clients MUST use `"directory"` when +* `kind`: OPTIONAL for LPP 1.1, LPP 1.2, and LPP 1.3. When omitted, it requests the + existing file-entry behavior. LPP 1.2 and 1.3 clients MUST use `"directory"` when the provider must discover the effective project entry from a directory; `"file"` may be used explicitly for file-entry behavior. @@ -351,7 +351,7 @@ Initialization and capability negotiation. The client MUST send | Field | Type | Description | | --- | --- | --- | -| `protocolVersion` | string | The protocol version the client wants to speak: `"1.0"`, `"1.1"`, or `"1.2"`. | +| `protocolVersion` | string | The protocol version the client wants to speak: `"1.0"`, `"1.1"`, `"1.2"`, or `"1.3"`. | | `clientInfo` | object, OPTIONAL | `{ "name": string, "version": string }` identifying the client. | ### 7.2 Result @@ -378,10 +378,10 @@ Initialization and capability negotiation. The client MUST send | Field | Type | Description | | --- | --- | --- | -| `protocolVersion` | string | The protocol version the provider will speak: `"1.0"`, `"1.1"`, or `"1.2"`. | +| `protocolVersion` | string | The protocol version the provider will speak: `"1.0"`, `"1.1"`, `"1.2"`, or `"1.3"`. | | `serverInfo` | object | `{ "name": string, "version": string }` identifying the provider. | | `languages` | array | One entry per source language the provider serves. | -| `capabilities` | object | One boolean field per capability. LPP 1.0 requires the eight fields listed below; LPP 1.1 and 1.2 additionally require `projectLoading`. | +| `capabilities` | object | One boolean field per capability. LPP 1.0 requires the eight fields listed below; LPP 1.1, 1.2, and 1.3 additionally require `projectLoading`; LPP 1.3 also defines `sourceIdentity`. | Each language entry: `{ "id": string, "extensions": [string] }`. `extensions` is the list of file extensions the provider associates with the language, @@ -403,7 +403,8 @@ extension. | `references` | `lpp/references` | Find references to the symbol at a position. | | `rename` | `lpp/rename` | Compute source edits for a semantic rename. | | `editValidation` | `lpp/validateEdits` | Validate a set of source edits against a document. | -| `projectLoading` | `lpp/check`, `lpp/compile` | Accept a client-selected entry or directory target and load its filesystem-backed source project. LPP 1.1 and 1.2. | +| `projectLoading` | `lpp/check`, `lpp/compile` | Accept a client-selected entry or directory target and load its filesystem-backed source project. LPP 1.1, 1.2, and 1.3. | +| `sourceIdentity` | `lpp/compile` | Return the provider-selected primary source identity for an entry-based compile result. LPP 1.3. | * The provider MUST set each capability to `true` only if it fully implements the corresponding method(s). @@ -443,8 +444,10 @@ when practical: The client then decides whether to terminate the session or restart with a supported version. LPP 1.0 clients MUST send `"1.0"`; clients using file-entry -project loading MUST send `"1.1"` or `"1.2"`; clients using directory targets -MUST send `"1.2"`. +project loading MUST send `"1.1"`, `"1.2"`, or `"1.3"`; clients using directory +targets MUST send `"1.2"` or `"1.3"`. A client that requires the +`sourceIdentity` capability MUST request `"1.3"` and require the provider to +advertise `sourceIdentity: true` before using entry-based compile results. ## 8. Common request parameters @@ -453,15 +456,16 @@ Document-scoped methods share this parameter shape: | Field | Type | Methods | Description | | --- | --- | --- | --- | | `documents` | DocumentSet | `check`, `compile`, `symbols`, `rename` | The documents to operate on. | -| `entry` | Project entry | `check`, `compile` in LPP 1.1 and 1.2 | Alternative to `documents`; asks the provider to load the source closure from the selected entry or directory target. | +| `entry` | Project entry | `check`, `compile` in LPP 1.1, 1.2, and 1.3 | Alternative to `documents`; asks the provider to load the source closure from the selected entry or directory target. | | `document` | Document | `definition`, `references`, `validateEdits` | The single document to operate on. | | `projectRoot` | string, OPTIONAL | `check`, `compile`, `symbols`, `rename` | URI identifying the project the documents belong to. Purely informational in v1; providers MUST accept and MAY use it. | -### 8.1 Entry-based project requests (LPP 1.1 and 1.2) +### 8.1 Entry-based project requests (LPP 1.1, 1.2, and 1.3) -In LPP 1.1 and 1.2, `lpp/check` and `lpp/compile` accept either `documents` or +In LPP 1.1, 1.2, and 1.3, `lpp/check` and `lpp/compile` accept either `documents` or `entry`, but not both. An `entry` request is available only when the provider -accepted protocol version `1.1` or `1.2` and advertised `projectLoading: true`. +accepted protocol version `1.1`, `1.2`, or `1.3` and advertised +`projectLoading: true`. The optional `projectRoot` field remains legal and is informational; the provider accepts it but determines the effective project root and source closure from the entry and the source language's rules. @@ -504,9 +508,9 @@ an LPP error of kind `projectLoadFailed`. The `details` object MUST contain `entryUri` and a provider-defined `reason`; a required-file failure SHOULD also include the affected `uri`. -### 8.2 Directory project requests (LPP 1.2) +### 8.2 Directory project requests (LPP 1.2 and 1.3) -LPP 1.2 extends the `entry` object with `kind: "directory"`: +LPP 1.2 and 1.3 extend the `entry` object with `kind: "directory"`: ```json { @@ -579,8 +583,9 @@ analyze every document in the set and MUST report all diagnostics found. ## 10. lpp/compile -Compile a document set into a single Workshop artifact. In LPP 1.1 and 1.2, an -entry-based request compiles the provider-loaded source closure as one unit; +Compile a document set into a single Workshop artifact. In LPP 1.1, 1.2, and +1.3, an entry-based request compiles the provider-loaded source closure as one +unit; the `compile.requiresSingleDocument` refusal applies only to a document-supplied request that contains more than one document. @@ -610,6 +615,13 @@ document-supplied request that contains more than one document. ``` * `diagnostics`: same shape as the `lpp/check` result. +* `sourceIdentity`: defined only in LPP 1.3. In an LPP 1.3 session, the + provider MUST advertise the `sourceIdentity` capability. If it advertises + `sourceIdentity: true`, an entry-based compile result MUST include a + lower-case SHA-256 hex digest of the provider-selected primary source text. + The provider owns effective entry selection. If the capability is false, the + field MUST be omitted. Document-supplied requests MAY omit it. LPP 1.1 and + 1.2 compile results MUST NOT include this field. * `artifact`: the compiled Workshop artifact, or `null`. * The `artifact` MUST be `null` whenever any error-severity diagnostic is reported. The provider MAY return `null` artifact in other failure cases. @@ -995,7 +1007,8 @@ clients MUST NOT parse `message`. * Protocol versions are strings of the form `MAJOR.MINOR` (for example `"1.0"`). LPP 1.0 is the first published version; LPP 1.1 adds file-entry - project loading and LPP 1.2 adds directory targets. + project loading, LPP 1.2 adds directory targets, and LPP 1.3 adds the + optional `sourceIdentity` capability for entry-based compile results. * `MAJOR` changes are breaking: message shapes, method semantics, or framing may change. A breaking change always produces a new MAJOR version, and clients and providers speaking different MAJOR versions are never expected @@ -1012,9 +1025,10 @@ clients MUST NOT parse `message`. * The provider either accepts it (echoing the version in the result) or fails with `protocolVersionMismatch` listing `supportedProtocolVersions`. * A client that receives the mismatch MUST pick the highest mutually supported - version and restart the session, or terminate. LPP 1.1 and 1.2 clients MAY - use the `projectLoading` capability; clients that need directory targets MUST - request `"1.2"`. + version and restart the session, or terminate. LPP 1.1, 1.2, and 1.3 clients + MAY use the `projectLoading` capability; clients that need directory targets + MUST request `"1.2"` or `"1.3"`. Clients that need source identity MUST + request `"1.3"` and require `sourceIdentity: true` in the result capabilities. * A provider MUST support at least one of the versions it lists in `supportedProtocolVersions`. @@ -1041,7 +1055,8 @@ contract: reconstruct, symbols, definition, references, rename, edit validation, project loading, errors/refusals, protocol mismatch, malformed messages, and shutdown. The same directory covers LPP 1.0, its LPP 1.1 file-entry - revision, and its LPP 1.2 directory-target revision. + revision, its LPP 1.2 directory-target revision, and its LPP 1.3 + source-identity revision. * `conformance/runner/`: a runner that replays fixtures against any provider binary and compares responses exactly. * `conformance/mock-provider/`: the reference provider for the demonstration @@ -1062,8 +1077,8 @@ Methods: | --- | --- | --- | --- | | `lpp/initialize` | none | `{ protocolVersion, clientInfo? }` | `{ protocolVersion, serverInfo, languages, capabilities }` | | `lpp/shutdown` | none | `{}` | `null` | -| `lpp/check` | `check`; plus `projectLoading` for an LPP 1.1/1.2 `entry` request | `{ documents, projectRoot? }` or `{ entry, projectRoot? }` | `{ documents: [{ uri, version, diagnostics }] }` | -| `lpp/compile` | `compile`; plus `projectLoading` for an LPP 1.1/1.2 `entry` request | `{ documents, projectRoot? }` or `{ entry, projectRoot? }` | `{ diagnostics: [{ uri, version, diagnostics }], artifact }` | +| `lpp/check` | `check`; plus `projectLoading` for an LPP 1.1/1.2/1.3 `entry` request | `{ documents, projectRoot? }` or `{ entry, projectRoot? }` | `{ documents: [{ uri, version, diagnostics }] }` | +| `lpp/compile` | `compile`; plus `projectLoading` for an LPP 1.1/1.2/1.3 `entry` request and `sourceIdentity` for an LPP 1.3 entry result | `{ documents, projectRoot? }` or `{ entry, projectRoot? }` | `{ diagnostics: [{ uri, version, diagnostics }], sourceIdentity?, artifact }` | | `lpp/reconstruct` | `reconstruct` | `{ artifact }` | `{ source, uri? }` | | `lpp/symbols` | `symbols` | `{ documents, projectRoot? }` | `{ documents: [{ uri, version, symbols }] }` | | `lpp/definition` | `definition` | `{ document, position }` | `{ locations }` |