Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
# Human review is required for what defines "passing" and for what publishes:
# CI and release workflows, public contracts, upstream pins, and agent instructions.
# Rules stay narrow so review is rare. Ordinary code, tests, and docs
# stay unowned, so agents may review and merge them.
/.github/CODEOWNERS @Teakowa
/.github/workflows/ @Teakowa

# The protocol spec is the public contract
/docs/spec/ @Teakowa

# Toolchain and release
/rust-toolchain.toml @Teakowa
/release-please-config.json @Teakowa
4 changes: 0 additions & 4 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
# Dependabot version updates for language-provider-protocol.
# Docs: https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
# Rust workspace: the root manifest governs both conformance crates.
- package-ecosystem: cargo
directory: /
schedule:
interval: monthly

# CI workflows and their action pins.
- package-ecosystem: github-actions
directory: /
schedule:
Expand Down
8 changes: 0 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,17 @@ serde = "1"
serde_json = "1"

[workspace.lints.rust]
# Unsafe operations inside an unsafe function still need an explicit block so
# the unsafe surface and its invariants remain visible at the operation site.
unsafe_op_in_unsafe_fn = "deny"
# Public items in a private module are not part of a usable crate API.
unreachable_pub = "deny"

[workspace.lints.clippy]
# These groups cover bug-prone findings without enabling broad style or
# restriction policies; CI promotes the remaining warning-level checks.
correctness = { level = "deny", priority = -1 }
suspicious = { level = "deny", priority = -1 }
perf = { level = "warn", priority = -1 }
# These development/progress macros must not reach protocol code.
dbg_macro = "deny"
todo = "deny"
unimplemented = "deny"

# Reduce dependency artifact volume while retaining line-table diagnostics for
# WrightKit code. See wrightkit/.github docs/rust-build-artifacts.md.
[profile.dev]
debug = 1

Expand Down
6 changes: 0 additions & 6 deletions conformance/common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
//! Helpers shared by the conformance runner and the reference mock provider.
//!
//! The runner expands `${PROJECT_URI}` in fixtures and the mock provider
//! resolves `entry` URIs back to paths; both sides must agree on the file-URI
//! codec byte for byte.

use std::path::{Path, PathBuf};

/// `file:` URI for an absolute path, percent-encoding bytes outside the
Expand Down
9 changes: 0 additions & 9 deletions conformance/mock-provider/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
//! `lpp-mock-provider`: the reference LPP provider for `x-demo-lang`.
//!
//! Layout: `server` owns session state and dispatch (the method table lives
//! next to the handlers in `methods`), `project` resolves `entry` targets
//! into document sets, `wire` holds the request param shapes, `rpc` builds
//! response envelopes, and `puzzle` is the x-demo-lang implementation.

mod methods;
mod project;
mod puzzle;
Expand Down Expand Up @@ -64,8 +57,6 @@ fn write_response(out: &mut impl Write, response: &Value) -> io::Result<()> {
out.flush()
}

/// `--protocol-version <v>` pins the supported version; `--without a,b,c`
/// disables capabilities (used by the capability-negotiation fixtures).
fn parse_args() -> (String, HashSet<&'static str>) {
let mut version = DEFAULT_PROTOCOL_VERSION.to_string();
let mut disabled = HashSet::new();
Expand Down
28 changes: 0 additions & 28 deletions conformance/mock-provider/src/methods.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
//! `lpp/*` method handlers and the `METHODS` dispatch table.
//!
//! Handlers share three pieces of plumbing: `documents_for_request` resolves
//! the document set (inline `documents` or a loaded `entry` project),
//! `parsed_set` checks and parses it in URI order, and `symbol_at_position`
//! resolves the document/position/symbol preamble that definition,
//! references, and rename share.

use std::collections::HashMap;

use serde_json::{Value, json};
Expand All @@ -25,15 +17,12 @@ use crate::wire::{
RenameParams, ResolveIdsParams, ValidateEditsParams,
};

/// A dispatchable `lpp/*` method: wire name, gating capability, handler.
pub(crate) struct Method {
pub name: &'static str,
pub capability: &'static str,
pub handler: fn(&Server, Value) -> Result<Value, HandlerError>,
}

/// Every `lpp/*` request method. Dispatch, capability gating, and the
/// `Method not found` fallback all route through this table.
pub(crate) const METHODS: &[Method] = &[
Method {
name: "lpp/check",
Expand Down Expand Up @@ -259,9 +248,6 @@ impl Server {
let edits: Vec<Value> = sorted_entries(&params.documents)
.into_iter()
.map(|(key, doc)| {
// §15.2: every reference in every received document must be
// covered, so a document that did not parse cleanly makes the
// rename unsafe — refuse instead of emitting partial edits.
let Some(puzzle) = parse_document(&doc.text).into_puzzle() else {
return Err(HandlerError::lpp(
"invalidDocument",
Expand Down Expand Up @@ -387,15 +373,10 @@ impl Server {
}
}

/// The most ids (calls plus enum members, duplicates counted) one
/// `lpp/resolveIds` request may carry.
const MAX_RESOLVE_IDS: usize = 1024;

/// The provider-side bound applied when a request omits `limit`.
const DEFAULT_LOOKUP_LIMIT: u32 = 20;

/// Reads `acceptedArtifactFormats` from `lpp/compile` params. Valid only
/// in LPP 1.4-or-later sessions, as a non-empty array of strings.
fn accepted_artifact_formats(
server: &Server,
params: &Value,
Expand All @@ -415,8 +396,6 @@ fn accepted_artifact_formats(
fn select_artifact_format(accepted: Option<Vec<String>>) -> Result<&'static str, HandlerError> {
match accepted {
None => Ok(ARTIFACT_FORMAT),
// The client's list is in preference order: the first accepted format
// we support wins.
Some(accepted) => accepted
.iter()
.find_map(|a| {
Expand Down Expand Up @@ -446,22 +425,19 @@ fn sha256_hex(text: &str) -> String {
})
}

/// Document-set entries in key order.
fn sorted_entries(map: &HashMap<String, Document>) -> Vec<(&String, &Document)> {
let mut entries: Vec<(&String, &Document)> = map.iter().collect();
entries.sort_by(|a, b| a.0.cmp(b.0));
entries
}

/// Documents in document-set key order.
fn sorted_docs(map: &HashMap<String, Document>) -> Vec<&Document> {
sorted_entries(map)
.into_iter()
.map(|(_, doc)| doc)
.collect()
}

/// Check and parse every document in document-set key order.
fn parsed_set(
map: &HashMap<String, Document>,
) -> Result<Vec<(&Document, ParseOutput)>, HandlerError> {
Expand Down Expand Up @@ -495,8 +471,6 @@ fn check_document(doc: &Document) -> Result<(), HandlerError> {
Ok(())
}

/// `{ uri, version, <key>: <value> }` — the per-document view shape shared by
/// check, compile, and symbols.
fn document_view(doc: &Document, key: &str, value: Value) -> Value {
let mut view = json!({ "uri": doc.uri, "version": doc.version });
view[key] = value;
Expand All @@ -507,8 +481,6 @@ fn location_json(uri: &str, range: Range) -> Value {
json!({ "uri": uri, "range": range })
}

/// The shared definition/references/rename preamble: document checks, then
/// the symbol under `position`, or the method's `noSymbolAtPosition` refusal.
fn symbol_at_position(
doc: &Document,
position: Position,
Expand Down
19 changes: 0 additions & 19 deletions conformance/mock-provider/src/project.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
//! Project loading: resolving an `entry` target (LPP 1.1 file, LPP 1.2
//! directory) into the in-memory document set that `lpp/check` and
//! `lpp/compile` consume.

use std::collections::HashMap;
use std::path::PathBuf;

Expand All @@ -12,9 +8,6 @@ use crate::rpc::HandlerError;
use crate::server::{LANGUAGE_ID, Server};
use crate::wire::{DocsParams, Document, ProjectEntry};

/// Resolve the document set for a `lpp/check`/`lpp/compile`-style request:
/// either the inline `documents` map or a project `entry` to load.
/// Returns the set and the canonical entry URI when loaded from a project.
pub(crate) fn documents_for_request(
server: &Server,
params: DocsParams,
Expand Down Expand Up @@ -50,15 +43,12 @@ enum Target {
Directory,
}

/// Project loading tied to one request's `ProjectEntry`. The shared error
/// constructors live here so call sites name only the reason and message.
struct Loader<'a> {
server: &'a Server,
entry: &'a ProjectEntry,
}

impl Loader<'_> {
/// `invalidEntry`: the request's entry object failed validation.
fn invalid(&self, reason: &str, message: impl Into<String>) -> HandlerError {
HandlerError::lpp(
"invalidEntry",
Expand All @@ -67,8 +57,6 @@ impl Loader<'_> {
)
}

/// `projectLoadFailed`: filesystem resolution failed. `uri` is the
/// concrete file URI when one is known.
fn fail(&self, reason: &str, uri: Option<&str>, message: impl Into<String>) -> HandlerError {
let mut details = json!({ "entryUri": self.entry.uri, "reason": reason });
if let Some(uri) = uri {
Expand Down Expand Up @@ -171,9 +159,6 @@ impl Loader<'_> {
)
})?;

// The project is the `.xdl` files in the selected entry's directory.
// A failed directory entry fails the load rather than silently
// dropping a required source (spec section 8.1).
let entries: Vec<_> = std::fs::read_dir(&root)
.map_err(|error| {
self.fail(
Expand All @@ -197,8 +182,6 @@ impl Loader<'_> {
if path.extension().is_none_or(|ext| ext != "xdl") {
continue;
}
// A directory named `*.xdl` is not a source file; symlinks
// still resolve (or fail) through canonicalization below.
let is_dir = entry
.file_type()
.map_err(|error| {
Expand All @@ -219,8 +202,6 @@ impl Loader<'_> {

let mut documents = HashMap::new();
for path in paths {
// The failing file's URI is knowable before canonicalization;
// `details.uri` should name it (spec section 8.1).
let unresolved_uri = path_to_file_uri(&path);
let path = std::fs::canonicalize(&path).map_err(|error| {
self.fail(
Expand Down
6 changes: 0 additions & 6 deletions conformance/mock-provider/src/puzzle.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
//! The `x-demo-lang` puzzle language: a document declares one equation puzzle
//! (target, start, named arithmetic ops, and a solution applying ops in
//! sequence). The language is deliberately unlike OPY and DEL so the mock
//! provider cannot accidentally satisfy two-language assumptions in LPP.

use serde::Serialize;

mod artifact;
Expand Down Expand Up @@ -133,7 +128,6 @@ pub(crate) struct ParseOutput {
}

impl ParseOutput {
/// The puzzle when the document parsed without error diagnostics.
pub(crate) fn into_puzzle(self) -> Option<Puzzle> {
match self.puzzle {
Some(puzzle) if !self.has_errors() => Some(puzzle),
Expand Down
9 changes: 0 additions & 9 deletions conformance/mock-provider/src/puzzle/artifact.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
//! Artifact formats produced by `lpp/compile` and consumed by
//! `lpp/reconstruct`. Artifact payloads are opaque to LPP; only the mock
//! provider interprets them.

use serde::Deserialize;
use serde_json::{Value, json};
use std::fmt::Write;

use super::Puzzle;

pub(crate) const ARTIFACT_FORMAT: &str = "x-demo/puzzle-eval-v1";
/// Compile-only format: `lpp/reconstruct` does not support it.
pub(crate) const SUMMARY_ARTIFACT_FORMAT: &str = "x-demo/puzzle-summary-v1";
pub(crate) const COMPILE_ARTIFACT_FORMATS: [&str; 2] = [ARTIFACT_FORMAT, SUMMARY_ARTIFACT_FORMAT];

Expand Down Expand Up @@ -48,16 +43,12 @@ struct SheetOp {
arg: i64,
}

/// The `x-demo-lang` identifier grammar: `[A-Za-z_][A-Za-z0-9_]*`.
fn is_ident(name: &str) -> bool {
let mut chars = name.chars();
matches!(chars.next(), Some(c) if c.is_ascii_alphabetic() || c == '_')
&& chars.all(|c| c.is_ascii_alphanumeric() || c == '_')
}

/// Canonical source text regenerated from a puzzle evaluation sheet.
/// Names are emitted verbatim, so every one must be a valid identifier or
/// the output would not parse (or would carry injected syntax).
pub(crate) fn reconstruct_source(content: &str) -> Option<String> {
let sheet: Sheet = serde_json::from_str(content).ok()?;
if !is_ident(&sheet.name)
Expand Down
Loading
Loading