#![cfg_attr(docsrs, feature(doc_cfg))]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct FileId(pub u32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct TextRange {
pub start: u32,
pub end: u32,
}
impl TextRange {
#[must_use]
pub const fn new(start: u32, end: u32) -> Self {
Self { start, end }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct FilePosition {
pub file: FileId,
pub offset: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Severity {
Error,
Warning,
Info,
Hint,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum DiagnosticSource {
#[default]
Syntax,
Type,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Diagnostic {
pub range: TextRange,
pub severity: Severity,
pub code: String,
pub message: String,
#[serde(default)]
pub source: DiagnosticSource,
#[serde(default)]
pub fixes: Vec<CodeAction>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SymbolKind {
Class,
Function,
Method,
Variable,
Constant,
Enum,
EnumMember,
Signal,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DocumentSymbol {
pub name: String,
pub detail: Option<String>,
pub kind: SymbolKind,
pub range: TextRange,
pub selection_range: TextRange,
pub children: Vec<DocumentSymbol>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum FoldKind {
Block,
Region,
Brackets,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct FoldRange {
pub range: TextRange,
pub kind: FoldKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CompletionKind {
Keyword,
Annotation,
Function,
Variable,
Constant,
Class,
Enum,
Signal,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CompletionItem {
pub label: String,
pub kind: CompletionKind,
pub insert_text: Option<String>,
#[serde(default)]
pub detail: Option<String>,
}
pub type Markdown = String;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HoverResult {
pub ty_label: Option<String>,
pub doc: Markdown,
pub range: TextRange,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ParamInfo {
pub label: String,
pub doc: Markdown,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SignatureInfo {
pub label: String,
pub doc: Markdown,
pub params: Vec<ParamInfo>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SignatureHelp {
pub signatures: Vec<SignatureInfo>,
pub active_signature: u32,
pub active_parameter: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum InlayHintKind {
Type,
Parameter,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct InlayHint {
pub offset: u32,
pub label: String,
pub kind: InlayHintKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum SemanticTokenType {
Function,
Method,
Variable,
Parameter,
Property,
Class,
Enum,
EnumMember,
Type,
Decorator,
Number,
String,
Comment,
Signal,
Constant,
}
pub mod semantic_token_modifier {
pub const DECLARATION: u32 = 1 << 0;
pub const READONLY: u32 = 1 << 1;
pub const STATIC: u32 = 1 << 2;
pub const DEFAULT_LIBRARY: u32 = 1 << 3;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct SemanticToken {
pub range: TextRange,
pub token_type: SemanticTokenType,
pub modifiers: u32,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TextEdit {
pub range: TextRange,
pub new_text: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FileEdit {
pub file: FileId,
pub edits: Vec<TextEdit>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SourceChange {
pub edits: Vec<FileEdit>,
}
impl SourceChange {
#[must_use]
pub fn single(file: FileId, edits: Vec<TextEdit>) -> Self {
Self {
edits: vec![FileEdit { file, edits }],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct FileRange {
pub file: FileId,
pub range: TextRange,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ReferenceKind {
Declaration,
Read,
Write,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Reference {
pub file: FileId,
pub range: TextRange,
pub kind: ReferenceKind,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum RenameError {
InvalidIdentifier {
new_name: String,
},
NotRenamable {
reason: String,
},
WouldCollide {
at: FileRange,
with: String,
},
CrossesUnsupportedBoundary {
what: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CodeAction {
pub title: String,
pub kind: Option<String>,
pub edit: SourceChange,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NavTarget {
pub file: FileId,
pub full_range: TextRange,
pub focus_range: TextRange,
pub name: String,
pub kind: SymbolKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Cancelled;
pub type Cancellable<T> = Result<T, Cancelled>;
#[derive(Debug, Clone)]
pub struct LineIndex {
line_starts: Vec<u32>,
len: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LineCol {
pub line: u32,
pub col: u32,
}
impl LineIndex {
#[must_use]
pub fn new(text: &str) -> Self {
let mut line_starts = vec![0u32];
for (i, b) in text.bytes().enumerate() {
if b == b'\n' {
#[allow(clippy::cast_possible_truncation)]
line_starts.push(i as u32 + 1);
}
}
#[allow(clippy::cast_possible_truncation)]
let len = text.len() as u32;
Self { line_starts, len }
}
#[must_use]
pub fn line_col(&self, offset: u32) -> LineCol {
let offset = offset.min(self.len);
let line = match self.line_starts.binary_search(&offset) {
Ok(line) => line,
Err(next) => next - 1,
};
#[allow(clippy::cast_possible_truncation)]
let line = line as u32;
LineCol {
line,
col: offset - self.line_starts[line as usize],
}
}
#[must_use]
pub fn utf16_col(&self, text: &str, offset: u32) -> u32 {
let lc = self.line_col(offset);
let line_start = self.line_starts[lc.line as usize] as usize;
let col_end = (line_start + lc.col as usize).min(text.len());
let units: usize = text[line_start..col_end].chars().map(char::len_utf16).sum();
u32::try_from(units).unwrap_or(u32::MAX)
}
#[must_use]
pub fn line_count(&self) -> u32 {
#[allow(clippy::cast_possible_truncation)]
{
self.line_starts.len() as u32
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn line_index_basics() {
let src = "ab\ncde\n\nx";
let idx = LineIndex::new(src);
assert_eq!(idx.line_count(), 4);
assert_eq!(idx.line_col(0), LineCol { line: 0, col: 0 });
assert_eq!(idx.line_col(1), LineCol { line: 0, col: 1 });
assert_eq!(idx.line_col(3), LineCol { line: 1, col: 0 }); assert_eq!(idx.line_col(7), LineCol { line: 2, col: 0 }); assert_eq!(idx.line_col(8), LineCol { line: 3, col: 0 }); }
#[test]
fn utf16_columns_account_for_astral_chars() {
let src = "a😀b";
let idx = LineIndex::new(src);
assert_eq!(idx.utf16_col(src, 0), 0); assert_eq!(idx.utf16_col(src, 1), 1); assert_eq!(idx.utf16_col(src, 5), 3); }
}