use std::path::PathBuf;
use strop_core::id::{BufferRevision, ByteColumn, DocumentId, LineIndex};
use strop_workspace::ResourceLocation;
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
pub enum Severity {
Error,
Warning,
Information,
Hint,
}
impl Severity {
pub const fn code(self) -> u8 {
match self {
Self::Error => 1,
Self::Warning => 2,
Self::Information => 3,
Self::Hint => 4,
}
}
pub const fn char(self) -> char {
match self {
Self::Error => 'E',
Self::Warning => 'W',
Self::Information => 'I',
Self::Hint => 'H',
}
}
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
#[serde(transparent)]
pub struct WireVersion(i32);
impl WireVersion {
pub const fn new(value: i32) -> Self {
Self(value)
}
pub const fn get(self) -> i32 {
self.0
}
pub(crate) fn next(self) -> Option<Self> {
self.0.checked_add(1).map(Self)
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Diag {
pub line: LineIndex,
pub col: ServerColumn,
pub end_line: LineIndex,
pub end_col: ServerColumn,
pub severity: Severity,
pub message: String,
}
impl Diag {
pub fn resolve(self, encoding: PositionEncoding, buffer: &strop_core::Buffer) -> ResolvedDiag {
let last = buffer.len_lines().saturating_sub(1);
let line = LineIndex::new(self.line.get().min(last));
let end_line = LineIndex::new(self.end_line.get().min(last));
let start_text = buffer.line_text(line);
let end_text = if end_line == line {
start_text.clone()
} else {
buffer.line_text(end_line)
};
ResolvedDiag {
line,
col: to_byte_col(&start_text, self.col, encoding),
end_line,
end_col: to_byte_col(&end_text, self.end_col, encoding),
severity: self.severity,
message: self.message,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ResolvedDiag {
pub line: LineIndex,
pub col: ByteColumn,
pub end_line: LineIndex,
pub end_col: ByteColumn,
pub severity: Severity,
pub message: String,
}
impl ResolvedDiag {
pub fn severity_char(&self) -> char {
self.severity.char()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub struct ServerId(u64);
impl ServerId {
pub const fn new(value: u64) -> Self {
Self(value)
}
pub const fn get(self) -> u64 {
self.0
}
pub(crate) fn allocate() -> Self {
static NEXT: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
match NEXT.fetch_update(
std::sync::atomic::Ordering::Relaxed,
std::sync::atomic::Ordering::Relaxed,
|n| n.checked_add(1),
) {
Ok(value) => Self(value),
Err(_) => panic!("LSP server identity exhausted"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub struct RequestId(u64);
impl RequestId {
pub const fn new(value: u64) -> Self {
Self(value)
}
pub const fn get(self) -> u64 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct RequestStamp {
pub request: RequestId,
pub server: ServerId,
pub document: DocumentId,
pub revision: BufferRevision,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum PositionEncoding {
Utf8,
Utf16,
}
pub fn to_server_col(line: &str, byte_col: ByteColumn, enc: PositionEncoding) -> ServerColumn {
crate::to_server_col_slice(line.into(), byte_col, enc)
}
pub fn to_byte_col(line: &str, server_col: ServerColumn, enc: PositionEncoding) -> ByteColumn {
crate::to_byte_col_slice(line.into(), server_col, enc)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum LocKind {
References,
Implementation,
TypeDefinition,
Declaration,
}
impl LocKind {
pub fn label(self) -> &'static str {
match self {
Self::References => "references",
Self::Implementation => "implementation",
Self::TypeDefinition => "type definition",
Self::Declaration => "declaration",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum RequestKind {
Goto,
Hover,
SwitchHeader,
Locations(LocKind),
Format,
Rename,
CodeAction,
DocumentSymbols,
WorkspaceSymbols,
}
impl RequestKind {
pub fn label(self) -> &'static str {
match self {
Self::Goto => "goto definition",
Self::Hover => "hover",
Self::SwitchHeader => "switch source/header",
Self::Locations(kind) => kind.label(),
Self::Format => "format",
Self::Rename => "rename",
Self::CodeAction => "code action",
Self::DocumentSymbols => "document symbols",
Self::WorkspaceSymbols => "workspace symbols",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum RequestRefusal {
NotOpen,
StaleRevision,
Unsupported,
NotReady,
IdentityExhausted,
Overloaded,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(transparent)]
pub struct ServerColumn(usize);
impl ServerColumn {
pub const fn new(value: usize) -> Self {
Self(value)
}
pub const fn get(self) -> usize {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ServerPosition {
pub line: LineIndex,
pub column: ServerColumn,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ProtoSymbol {
pub name: String,
pub container: String,
pub kind: String,
pub location: ServerLocation,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ServerLocation {
pub doc: ResourceLocation,
pub position: ServerPosition,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ServerEdit {
pub start: ServerPosition,
pub end: ServerPosition,
pub new_text: String,
}
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ProtoAction {
pub title: String,
pub edits: Option<Vec<(ResourceLocation, Vec<ServerEdit>)>>,
pub has_external_command: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct ReplyContext {
pub stamp: RequestStamp,
pub encoding: PositionEncoding,
pub kind: RequestKind,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct RequestInput {
pub document: DocumentId,
pub revision: BufferRevision,
#[serde(with = "strop_core::path_serde")]
pub path: PathBuf,
pub line: LineIndex,
pub byte_col: ByteColumn,
pub line_text: crate::FrozenLine,
pub kind: RequestKind,
#[serde(default)]
pub rename_to: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PendingRequest {
pub stamp: RequestStamp,
pub input: RequestInput,
#[serde(default)]
pub tab_width: Option<usize>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct DiagnosticContext {
pub server: ServerId,
pub document: DocumentId,
pub revision: BufferRevision,
pub encoding: PositionEncoding,
pub version: Option<WireVersion>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum LspEvent {
Diagnostics {
context: DiagnosticContext,
doc: ResourceLocation,
diags: Vec<Diag>,
},
Ready {
server: ServerId,
name: String,
},
Failed {
server: ServerId,
name: String,
hint: String,
},
ServerMessage {
server: ServerId,
name: String,
text: String,
},
HoverText {
context: ReplyContext,
text: String,
},
GotoLocation {
context: ReplyContext,
location: ServerLocation,
},
Locations {
context: ReplyContext,
kind: LocKind,
items: Vec<ServerLocation>,
},
Edits {
context: ReplyContext,
edits: Vec<ServerEdit>,
},
WorkspaceEdits {
context: ReplyContext,
edits: Vec<(ResourceLocation, Vec<ServerEdit>)>,
},
ActionList {
context: ReplyContext,
actions: Vec<ProtoAction>,
},
Symbols {
context: ReplyContext,
symbols: Vec<ProtoSymbol>,
},
WorkspaceSymbols {
server: ServerId,
generation: u64,
symbols: Vec<ProtoSymbol>,
},
WorkspaceSymbolsFailed {
server: ServerId,
generation: u64,
reason: String,
},
Note {
context: ReplyContext,
text: String,
},
}