Skip to content

Commit

Permalink
Add cell field to JSON output format
Browse files Browse the repository at this point in the history
  • Loading branch information
dhruvmanila committed Sep 26, 2023
1 parent a91285c commit a5a2f5b
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 12 deletions.
41 changes: 31 additions & 10 deletions crates/ruff_linter/src/message/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use serde::{Serialize, Serializer};
use serde_json::{json, Value};

use ruff_diagnostics::Edit;
use ruff_notebook::NotebookIndex;
use ruff_source_file::SourceCode;
use ruff_text_size::Ranged;

Expand All @@ -19,16 +20,17 @@ impl Emitter for JsonEmitter {
&mut self,
writer: &mut dyn Write,
messages: &[Message],
_context: &EmitterContext,
context: &EmitterContext,
) -> anyhow::Result<()> {
serde_json::to_writer_pretty(writer, &ExpandedMessages { messages })?;
serde_json::to_writer_pretty(writer, &ExpandedMessages { messages, context })?;

Ok(())
}
}

struct ExpandedMessages<'a> {
messages: &'a [Message],
context: &'a EmitterContext<'a>,
}

impl Serialize for ExpandedMessages<'_> {
Expand All @@ -39,34 +41,44 @@ impl Serialize for ExpandedMessages<'_> {
let mut s = serializer.serialize_seq(Some(self.messages.len()))?;

for message in self.messages {
let value = message_to_json_value(message);
let value = message_to_json_value(message, self.context);
s.serialize_element(&value)?;
}

s.end()
}
}

pub(crate) fn message_to_json_value(message: &Message) -> Value {
pub(crate) fn message_to_json_value(message: &Message, context: &EmitterContext) -> Value {
let source_code = message.file.to_source_code();
let notebook_index = context.notebook_index(message.filename());

let fix = message.fix.as_ref().map(|fix| {
json!({
"applicability": fix.applicability(),
"message": message.kind.suggestion.as_deref(),
"edits": &ExpandedEdits { edits: fix.edits(), source_code: &source_code },
"edits": &ExpandedEdits { edits: fix.edits(), source_code: &source_code, notebook_index },
})
});

let start_location = source_code.source_location(message.start());
let end_location = source_code.source_location(message.end());
let noqa_location = source_code.source_location(message.noqa_offset);
let mut start_location = source_code.source_location(message.start());
let mut end_location = source_code.source_location(message.end());
let mut noqa_location = source_code.source_location(message.noqa_offset);
let mut notebook_cell_index = None;

if let Some(notebook_index) = notebook_index {
notebook_cell_index = Some(notebook_index.cell(start_location.row.get()).unwrap_or(1));
start_location = notebook_index.translated_location(&start_location);
end_location = notebook_index.translated_location(&end_location);
noqa_location = notebook_index.translated_location(&noqa_location);
}

json!({
"code": message.kind.rule().noqa_code().to_string(),
"url": message.kind.rule().url(),
"message": message.kind.body,
"fix": fix,
"cell": notebook_cell_index,
"location": start_location,
"end_location": end_location,
"filename": message.filename(),
Expand All @@ -77,6 +89,7 @@ pub(crate) fn message_to_json_value(message: &Message) -> Value {
struct ExpandedEdits<'a> {
edits: &'a [Edit],
source_code: &'a SourceCode<'a, 'a>,
notebook_index: Option<&'a NotebookIndex>,
}

impl Serialize for ExpandedEdits<'_> {
Expand All @@ -87,10 +100,18 @@ impl Serialize for ExpandedEdits<'_> {
let mut s = serializer.serialize_seq(Some(self.edits.len()))?;

for edit in self.edits {
let mut location = self.source_code.source_location(edit.start());
let mut end_location = self.source_code.source_location(edit.end());

if let Some(notebook_index) = self.notebook_index {
location = notebook_index.translated_location(&location);
end_location = notebook_index.translated_location(&end_location);
}

let value = json!({
"content": edit.content().unwrap_or_default(),
"location": self.source_code.source_location(edit.start()),
"end_location": self.source_code.source_location(edit.end())
"location": location,
"end_location": end_location
});

s.serialize_element(&value)?;
Expand Down
4 changes: 2 additions & 2 deletions crates/ruff_linter/src/message/json_lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ impl Emitter for JsonLinesEmitter {
&mut self,
writer: &mut dyn Write,
messages: &[Message],
_context: &EmitterContext,
context: &EmitterContext,
) -> anyhow::Result<()> {
let mut w = writer;
for message in messages {
serde_json::to_writer(&mut w, &message_to_json_value(message))?;
serde_json::to_writer(&mut w, &message_to_json_value(message, context))?;
w.write_all(b"\n")?;
}
Ok(())
Expand Down
14 changes: 14 additions & 0 deletions crates/ruff_notebook/src/index.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use serde::{Deserialize, Serialize};

use ruff_source_file::{OneIndexed, SourceLocation};

/// Jupyter Notebook indexing table
///
/// When we lint a jupyter notebook, we have to translate the row/column based on
Expand All @@ -23,4 +25,16 @@ impl NotebookIndex {
pub fn cell_row(&self, row: usize) -> Option<u32> {
self.row_to_row_in_cell.get(row).copied()
}

/// Translates the given source location based on the indexing table.
///
/// This will translate the row/column in the concatenated source code
/// to the row/column in the Jupyter Notebook.
pub fn translated_location(&self, source_location: &SourceLocation) -> SourceLocation {
SourceLocation {
row: OneIndexed::new(self.cell_row(source_location.row.get()).unwrap_or(1) as usize)
.unwrap(),
column: source_location.column,
}
}
}

0 comments on commit a5a2f5b

Please sign in to comment.