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 2620f06
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 18 deletions.
18 changes: 14 additions & 4 deletions crates/cairo-lang-language-server/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ pub struct Config {
///
/// The property is set by the user under the `cairo1.corelibPath` key in client configuration.
pub unmanaged_core_path: Option<PathBuf>,
/// Whether to include the diagnostic mapping as a note for all diagnostic locations.
///
/// The property is set by the user under the `cairo1.includeDiagMapping` key in client
/// configuration.
pub include_diag_mapping: bool,
}

impl Config {
Expand All @@ -42,10 +47,13 @@ impl Config {
return;
}

let items = vec![ConfigurationItem {
scope_uri: None,
section: Some("cairo1.corelibPath".to_owned()),
}];
let items = vec![
ConfigurationItem { scope_uri: None, section: Some("cairo1.corelibPath".to_owned()) },
ConfigurationItem {
scope_uri: None,
section: Some("cairo1.includeDiagMapping".to_owned()),
},
];
let expected_len = items.len();
if let Ok(response) = client
.configuration(items)
Expand All @@ -71,6 +79,8 @@ impl Config {
.and_then(Value::as_str)
.filter(|s| !s.is_empty())
.map(Into::into);
self.include_diag_mapping =
response.pop_front().as_ref().and_then(Value::as_bool).unwrap_or_default();

debug!("reloaded configuration: {self:#?}");
}
Expand Down
56 changes: 45 additions & 11 deletions crates/cairo-lang-language-server/src/lang/diagnostics/lsp.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
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,
Expand All @@ -14,35 +15,46 @@ pub fn map_cairo_diagnostics_to_lsp<T: DiagnosticEntry>(
db: &T::DbType,
diags: &mut Vec<Diagnostic>,
diagnostics: &Diagnostics<T>,
include_diag_mapping: bool,
) {
for diagnostic in diagnostics.get_diagnostics_without_duplicates(db) {
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 +65,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
22 changes: 19 additions & 3 deletions crates/cairo-lang-language-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,9 +452,25 @@ impl Backend {
drop(state);

let mut diags = Vec::new();
map_cairo_diagnostics_to_lsp((*db).upcast(), &mut diags, &new_file_diagnostics.parser);
map_cairo_diagnostics_to_lsp((*db).upcast(), &mut diags, &new_file_diagnostics.semantic);
map_cairo_diagnostics_to_lsp((*db).upcast(), &mut diags, &new_file_diagnostics.lowering);
let include_diag_mapping = self.config.read().await.include_diag_mapping;
map_cairo_diagnostics_to_lsp(
(*db).upcast(),
&mut diags,
&new_file_diagnostics.parser,
include_diag_mapping,
);
map_cairo_diagnostics_to_lsp(
(*db).upcast(),
&mut diags,
&new_file_diagnostics.semantic,
include_diag_mapping,
);
map_cairo_diagnostics_to_lsp(
(*db).upcast(),
&mut diags,
&new_file_diagnostics.lowering,
include_diag_mapping,
);

// Drop database snapshot before we wait for the client responding to our notification.
drop(db);
Expand Down
5 changes: 5 additions & 0 deletions vscode-cairo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@
"description": "Absolute path to the Cairo core library, used in non-Scarb projects.",
"scope": "window"
},
"cairo1.includeDiagMapping": {
"type": "boolean",
"description": "Enable getting unmapped diagnostics information.",
"scope": "window"
},
"cairo1.languageServerExtraEnv": {
"type": [
"null",
Expand Down

0 comments on commit 2620f06

Please sign in to comment.