Skip to content

Commit

Permalink
Allowed to get the diagnostics mappings in the LS.
Browse files Browse the repository at this point in the history
  • Loading branch information
orizi committed Aug 26, 2024
1 parent 401d763 commit 33730b9
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 11 deletions.
6 changes: 6 additions & 0 deletions crates/cairo-lang-language-server/src/env_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use std::time::Duration;
use tracing::debug;

pub const CAIRO_LS_DB_REPLACE_INTERVAL: &'_ str = "CAIRO_LS_DB_REPLACE_INTERVAL";
pub const CAIRO_LS_INCLUDE_DIAG_MAPPING: &'_ str = "CAIRO_LS_INCLUDE_DIAG_MAPPING";
pub const CAIRO_LS_LOG: &'_ str = "CAIRO_LS_LOG";
pub const CAIRO_LS_PROFILE: &'_ str = "CAIRO_LS_PROFILE";
pub const SCARB: &'_ str = "SCARB";
Expand All @@ -28,6 +29,11 @@ pub fn db_replace_interval() -> Duration {
.unwrap_or_else(|| Duration::from_secs(DEFAULT))
}

/// Whether to include the diagnostic mapping as a note for all diagnostic locations.
pub fn include_diag_mapping() -> bool {
env::var_os(CAIRO_LS_INCLUDE_DIAG_MAPPING).map(env_to_bool).unwrap_or_default()
}

/// LS tracing filter, see [`tracing_subscriber::EnvFilter`] for more.
pub fn log_env_filter() -> String {
env::var(CAIRO_LS_LOG).unwrap_or_default()
Expand Down
57 changes: 46 additions & 11 deletions crates/cairo-lang-language-server/src/lang/diagnostics/lsp.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use cairo_lang_diagnostics::{DiagnosticEntry, DiagnosticLocation, Diagnostics, Severity};
use cairo_lang_filesystem::db::FilesGroup;
use cairo_lang_filesystem::ids::FileId;
use cairo_lang_utils::Upcast;
use tower_lsp::lsp_types::{
Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, Location, NumberOrString, Range,
};
use tracing::error;

use crate::env_config::include_diag_mapping;
use crate::lang::lsp::{LsProtoGroup, ToLsp};

/// Converts internal diagnostics to LSP format.
Expand All @@ -15,34 +17,45 @@ pub fn map_cairo_diagnostics_to_lsp<T: DiagnosticEntry>(
diags: &mut Vec<Diagnostic>,
diagnostics: &Diagnostics<T>,
) {
for diagnostic in diagnostics.get_diagnostics_without_duplicates(db) {
let include_diag_mapping = include_diag_mapping();
for diagnostic in if include_diag_mapping {
diagnostics.get_all()
} else {
diagnostics.get_diagnostics_without_duplicates(db)
} {
let mut message = diagnostic.format(db);
let mut related_information = vec![];
for note in diagnostic.notes(db) {
if let Some(location) = &note.location {
let Some(range) = get_range(db.upcast(), location) else {
let Some((range, file_id)) = get_mapped_range_and_add_mapping_note(
db,
location,
include_diag_mapping.then_some(&mut related_information),
"Next note mapped from here.",
) else {
continue;
};
related_information.push(DiagnosticRelatedInformation {
location: Location { uri: db.url_for_file(location.file_id), range },
location: Location { uri: db.url_for_file(file_id), range },
message: note.text.clone(),
});
} else {
message += &format!("\nnote: {}", note.text);
}
}

let Some(range) = get_range(db.upcast(), &diagnostic.location(db)) else {
let Some((range, _)) = get_mapped_range_and_add_mapping_note(
db,
&diagnostic.location(db),
include_diag_mapping.then_some(&mut related_information),
"Diagnostic mapped from here.",
) else {
continue;
};
diags.push(Diagnostic {
range,
message,
related_information: if related_information.is_empty() {
None
} else {
Some(related_information)
},
related_information: (!related_information.is_empty()).then_some(related_information),
severity: Some(match diagnostic.severity() {
Severity::Error => DiagnosticSeverity::ERROR,
Severity::Warning => DiagnosticSeverity::WARNING,
Expand All @@ -53,9 +66,31 @@ pub fn map_cairo_diagnostics_to_lsp<T: DiagnosticEntry>(
}
}

/// Returns the mapped range of a location, optionally adds a note about the mapping of the
/// location.
fn get_mapped_range_and_add_mapping_note(
db: &(impl Upcast<dyn FilesGroup> + ?Sized),
orig: &DiagnosticLocation,
related_info: Option<&mut Vec<DiagnosticRelatedInformation>>,
message: &str,
) -> Option<(Range, FileId)> {
let mapped = orig.user_location(db.upcast());
let mapped_range = get_lsp_range(db.upcast(), &mapped)?;
if let Some(related_info) = related_info {
if *orig != mapped {
if let Some(range) = get_lsp_range(db.upcast(), orig) {
related_info.push(DiagnosticRelatedInformation {
location: Location { uri: db.url_for_file(orig.file_id), range },
message: message.to_string(),
});
}
}
}
Some((mapped_range, mapped.file_id))
}

/// Converts an internal diagnostic location to an LSP range.
fn get_range(db: &dyn FilesGroup, location: &DiagnosticLocation) -> Option<Range> {
let location = location.user_location(db);
fn get_lsp_range(db: &dyn FilesGroup, location: &DiagnosticLocation) -> Option<Range> {
let Some(span) = location.span.position_in_file(db, location.file_id) else {
error!("failed to get range for diagnostic");
return None;
Expand Down

0 comments on commit 33730b9

Please sign in to comment.