mirror of
https://github.com/openharmony/third_party_rust_codespan.git
synced 2026-07-20 00:53:34 -04:00
feat: Make codespan_lsp use codespan_reporting
Makes `codespan_lsp` more broadly usable by lifting the `codespan` restriction. `codespan` users can still used it after converting to and from usize for arguments and return values. BREAKING CHANGE The signatures now take usize instead of `codespan`s explicit, byte, line, column types. The error types have been copied into codespan_lsp to remove the dependency on codespan.
This commit is contained in:
@@ -10,7 +10,7 @@ documentation = "https://docs.rs/codespan-lsp"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
codespan = { version = "0.9.5", path = "../codespan" }
|
||||
codespan-reporting = { version = "0.9.5", path = "../codespan-reporting" }
|
||||
# WARNING: Be extremely careful when expanding this version range.
|
||||
# We should be confident that all of the uses of `lsp-types` in `codespan-lsp`
|
||||
# will be valid for all the versions in this range. Getting this range wrong
|
||||
@@ -18,3 +18,6 @@ codespan = { version = "0.9.5", path = "../codespan" }
|
||||
# absolute no-no, breaking much of what we enjoy about Cargo!
|
||||
lsp-types = ">=0.70, <0.75"
|
||||
url = "2"
|
||||
|
||||
[dev-dependencies]
|
||||
codespan = { version = "0.9.5", path = "../codespan" }
|
||||
|
||||
+147
-69
@@ -1,47 +1,97 @@
|
||||
//! Utilities for translating from codespan types into Language Server Protocol (LSP) types
|
||||
|
||||
use codespan::{
|
||||
ByteIndex, ByteOffset, ColumnIndex, FileId, Files, LineIndex, LineIndexOutOfBoundsError,
|
||||
LocationError, RawIndex, RawOffset, Span, SpanOutOfBoundsError,
|
||||
};
|
||||
use std::{error, fmt, ops::Range};
|
||||
|
||||
use codespan_reporting::files::Files;
|
||||
|
||||
// WARNING: Be extremely careful when adding new imports here, as it could break
|
||||
// the compatible version range that we claim in our `Cargo.toml`. This could
|
||||
// potentially break down-stream builds on a `cargo update`. This is an
|
||||
// absolute no-no, breaking much of what we enjoy about Cargo!
|
||||
use lsp_types::{Position as LspPosition, Range as LspRange};
|
||||
use std::ffi::OsString;
|
||||
use std::path::PathBuf;
|
||||
use std::{error, fmt};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
UnableToCorrelateFilename(OsString),
|
||||
ColumnOutOfBounds {
|
||||
given: ColumnIndex,
|
||||
max: ColumnIndex,
|
||||
},
|
||||
ColumnOutOfBounds { given: usize, max: usize },
|
||||
Location(LocationError),
|
||||
LineIndexOutOfBounds(LineIndexOutOfBoundsError),
|
||||
SpanOutOfBounds(SpanOutOfBoundsError),
|
||||
MissingFile,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Error::UnableToCorrelateFilename(s) => {
|
||||
let p = PathBuf::from(s);
|
||||
write!(f, "Unable to correlate filename `{}` to url", p.display())
|
||||
}
|
||||
Error::ColumnOutOfBounds { given, max } => {
|
||||
write!(f, "Column out of bounds - given: {}, max: {}", given, max)
|
||||
}
|
||||
Error::Location(e) => e.fmt(f),
|
||||
Error::LineIndexOutOfBounds(e) => e.fmt(f),
|
||||
Error::SpanOutOfBounds(e) => e.fmt(f),
|
||||
Error::MissingFile => write!(f, "File does not exit"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct LineIndexOutOfBoundsError {
|
||||
pub given: usize,
|
||||
pub max: usize,
|
||||
}
|
||||
|
||||
impl error::Error for LineIndexOutOfBoundsError {}
|
||||
|
||||
impl fmt::Display for LineIndexOutOfBoundsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Line index out of bounds - given: {}, max: {}",
|
||||
self.given, self.max
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum LocationError {
|
||||
OutOfBounds { given: usize, span: Range<usize> },
|
||||
InvalidCharBoundary { given: usize },
|
||||
}
|
||||
|
||||
impl error::Error for LocationError {}
|
||||
|
||||
impl fmt::Display for LocationError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
LocationError::OutOfBounds { given, span } => write!(
|
||||
f,
|
||||
"Byte index out of bounds - given: {}, span: {}..{}",
|
||||
given, span.start, span.end
|
||||
),
|
||||
LocationError::InvalidCharBoundary { given } => {
|
||||
write!(f, "Byte index within character boundary - given: {}", given)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct SpanOutOfBoundsError {
|
||||
pub given: Range<usize>,
|
||||
pub span: Range<usize>,
|
||||
}
|
||||
|
||||
impl error::Error for SpanOutOfBoundsError {}
|
||||
|
||||
impl fmt::Display for SpanOutOfBoundsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Span out of bounds - given: {}..{}, span: {}..{}",
|
||||
self.given.start, self.given.end, self.span.start, self.span.end
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LocationError> for Error {
|
||||
fn from(e: LocationError) -> Error {
|
||||
Error::Location(e)
|
||||
@@ -63,7 +113,7 @@ impl From<SpanOutOfBoundsError> for Error {
|
||||
impl error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
match self {
|
||||
Error::UnableToCorrelateFilename(_) | Error::ColumnOutOfBounds { .. } => None,
|
||||
Error::ColumnOutOfBounds { .. } | Error::MissingFile => None,
|
||||
Error::Location(error) => Some(error),
|
||||
Error::LineIndexOutOfBounds(error) => Some(error),
|
||||
Error::SpanOutOfBounds(error) => Some(error),
|
||||
@@ -73,61 +123,82 @@ impl error::Error for Error {
|
||||
|
||||
fn location_to_position(
|
||||
line_str: &str,
|
||||
line: LineIndex,
|
||||
column: ColumnIndex,
|
||||
byte_index: ByteIndex,
|
||||
line: usize,
|
||||
column: usize,
|
||||
byte_index: usize,
|
||||
) -> Result<LspPosition, Error> {
|
||||
if column.to_usize() > line_str.len() {
|
||||
let max = ColumnIndex(line_str.len() as RawIndex);
|
||||
if column > line_str.len() {
|
||||
let max = line_str.len();
|
||||
let given = column;
|
||||
|
||||
Err(Error::ColumnOutOfBounds { given, max })
|
||||
} else if !line_str.is_char_boundary(column.to_usize()) {
|
||||
} else if !line_str.is_char_boundary(column) {
|
||||
let given = byte_index;
|
||||
|
||||
Err(LocationError::InvalidCharBoundary { given }.into())
|
||||
} else {
|
||||
let line_utf16 = line_str[..column.to_usize()].encode_utf16();
|
||||
let line_utf16 = line_str[..column].encode_utf16();
|
||||
let character = line_utf16.count() as u64;
|
||||
let line = line.to_usize() as u64;
|
||||
let line = line as u64;
|
||||
|
||||
Ok(LspPosition { line, character })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn byte_index_to_position<Source: AsRef<str>>(
|
||||
files: &Files<Source>,
|
||||
file_id: FileId,
|
||||
byte_index: ByteIndex,
|
||||
) -> Result<LspPosition, Error> {
|
||||
let location = files.location(file_id, byte_index)?;
|
||||
let line_span = files.line_span(file_id, location.line)?;
|
||||
let line_str = files.source_slice(file_id, line_span)?;
|
||||
let column = ColumnIndex::from((byte_index - line_span.start()).0 as RawIndex);
|
||||
pub fn byte_index_to_position<'a, F>(
|
||||
files: &'a F,
|
||||
file_id: F::FileId,
|
||||
byte_index: usize,
|
||||
) -> Result<LspPosition, Error>
|
||||
where
|
||||
F: Files<'a> + ?Sized,
|
||||
{
|
||||
let source = files.source(file_id).ok_or_else(|| Error::MissingFile)?;
|
||||
let source = source.as_ref();
|
||||
|
||||
location_to_position(line_str, location.line, column, byte_index)
|
||||
let line_index =
|
||||
files
|
||||
.line_index(file_id, byte_index)
|
||||
.ok_or_else(|| LineIndexOutOfBoundsError {
|
||||
given: byte_index,
|
||||
max: source.lines().count(),
|
||||
})?;
|
||||
let line_span = files.line_range(file_id, line_index).unwrap();
|
||||
|
||||
let line_str = source
|
||||
.get(line_span.clone())
|
||||
.ok_or_else(|| SpanOutOfBoundsError {
|
||||
given: line_span.clone(),
|
||||
span: 0..source.len(),
|
||||
})?;
|
||||
let column = byte_index - line_span.start;
|
||||
|
||||
location_to_position(line_str, line_index, column, byte_index)
|
||||
}
|
||||
|
||||
pub fn byte_span_to_range<Source: AsRef<str>>(
|
||||
files: &Files<Source>,
|
||||
file_id: FileId,
|
||||
span: Span,
|
||||
) -> Result<LspRange, Error> {
|
||||
pub fn byte_span_to_range<'a, F>(
|
||||
files: &'a F,
|
||||
file_id: F::FileId,
|
||||
span: Range<usize>,
|
||||
) -> Result<LspRange, Error>
|
||||
where
|
||||
F: Files<'a> + ?Sized,
|
||||
{
|
||||
Ok(LspRange {
|
||||
start: byte_index_to_position(files, file_id, span.start())?,
|
||||
end: byte_index_to_position(files, file_id, span.end())?,
|
||||
start: byte_index_to_position(files, file_id, span.start)?,
|
||||
end: byte_index_to_position(files, file_id, span.end)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn character_to_line_offset(line: &str, character: u64) -> Result<ByteOffset, Error> {
|
||||
let line_len = ByteOffset::from(line.len() as RawOffset);
|
||||
pub fn character_to_line_offset(line: &str, character: u64) -> Result<usize, Error> {
|
||||
let line_len = line.len();
|
||||
let mut character_offset = 0;
|
||||
|
||||
let mut chars = line.chars();
|
||||
while let Some(ch) = chars.next() {
|
||||
if character_offset == character {
|
||||
let chars_off = ByteOffset::from_str_len(chars.as_str());
|
||||
let ch_off = ByteOffset::from_char_len(ch);
|
||||
let chars_off = chars.as_str().len();
|
||||
let ch_off = ch.len_utf8();
|
||||
|
||||
return Ok(line_len - chars_off - ch_off);
|
||||
}
|
||||
@@ -140,38 +211,45 @@ pub fn character_to_line_offset(line: &str, character: u64) -> Result<ByteOffset
|
||||
Ok(line_len)
|
||||
} else {
|
||||
Err(Error::ColumnOutOfBounds {
|
||||
given: ColumnIndex(character_offset as RawIndex),
|
||||
max: ColumnIndex(line.len() as RawIndex),
|
||||
given: character_offset as usize,
|
||||
max: line.len(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn position_to_byte_index<Source: AsRef<str>>(
|
||||
files: &Files<Source>,
|
||||
file_id: FileId,
|
||||
pub fn position_to_byte_index<'a, F>(
|
||||
files: &'a F,
|
||||
file_id: F::FileId,
|
||||
position: &LspPosition,
|
||||
) -> Result<ByteIndex, Error> {
|
||||
let line_span = files.line_span(file_id, position.line as RawIndex)?;
|
||||
let source = files.source_slice(file_id, line_span)?;
|
||||
) -> Result<usize, Error>
|
||||
where
|
||||
F: Files<'a> + ?Sized,
|
||||
{
|
||||
let source = files.source(file_id).ok_or_else(|| Error::MissingFile)?;
|
||||
let source = source.as_ref();
|
||||
|
||||
let line_span = files.line_range(file_id, position.line as usize).unwrap();
|
||||
|
||||
let byte_offset = character_to_line_offset(source, position.character)?;
|
||||
|
||||
Ok(line_span.start() + byte_offset)
|
||||
Ok(line_span.start + byte_offset)
|
||||
}
|
||||
|
||||
pub fn range_to_byte_span<Source: AsRef<str>>(
|
||||
files: &Files<Source>,
|
||||
file_id: FileId,
|
||||
pub fn range_to_byte_span<'a, F>(
|
||||
files: &'a F,
|
||||
file_id: F::FileId,
|
||||
range: &LspRange,
|
||||
) -> Result<Span, Error> {
|
||||
Ok(Span::new(
|
||||
position_to_byte_index(files, file_id, &range.start)?,
|
||||
position_to_byte_index(files, file_id, &range.end)?,
|
||||
))
|
||||
) -> Result<Range<usize>, Error>
|
||||
where
|
||||
F: Files<'a> + ?Sized,
|
||||
{
|
||||
Ok(position_to_byte_index(files, file_id, &range.start)?
|
||||
..position_to_byte_index(files, file_id, &range.end)?)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use codespan::Location;
|
||||
use codespan::{Files, Location};
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -192,7 +270,7 @@ test
|
||||
character: 2,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
.unwrap() as u32;
|
||||
assert_eq!(Location::new(3, 2), files.location(file_id, pos).unwrap());
|
||||
}
|
||||
|
||||
@@ -213,7 +291,7 @@ test
|
||||
character: 3,
|
||||
},
|
||||
);
|
||||
assert_eq!(result, Ok(ByteIndex::from(5)));
|
||||
assert_eq!(result, Ok(5));
|
||||
|
||||
let result = position_to_byte_index(
|
||||
&files,
|
||||
@@ -223,7 +301,7 @@ test
|
||||
character: 6,
|
||||
},
|
||||
);
|
||||
assert_eq!(result, Ok(ByteIndex::from(10)));
|
||||
assert_eq!(result, Ok(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -231,7 +309,7 @@ test
|
||||
let mut files = Files::new();
|
||||
let file_id = files.add("unicode", UNICODE);
|
||||
|
||||
let result = byte_index_to_position(&files, file_id, ByteIndex::from(5));
|
||||
let result = byte_index_to_position(&files, file_id, 5);
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(LspPosition {
|
||||
@@ -240,7 +318,7 @@ test
|
||||
})
|
||||
);
|
||||
|
||||
let result = byte_index_to_position(&files, file_id, ByteIndex::from(10));
|
||||
let result = byte_index_to_position(&files, file_id, 10);
|
||||
assert_eq!(
|
||||
result,
|
||||
Ok(LspPosition {
|
||||
|
||||
Reference in New Issue
Block a user