feat: use rslint_errors Diagnostic in rome_analyze
MODIFY
Cargo.lock
Cargo.lock
@@ -1296,6 +1296,7 @@ dependencies = [
"lspower",
"rome_analyze",
"rome_formatter",
+ "rslint_errors",
"rslint_parser",
"serde",
"serde_json",

MODIFY
crates/rome_analyze/src/analysis_server.rs
crates/rome_analyze/src/analysis_server.rs
use std::{collections::HashMap, sync::Arc};
-use anyhow::Result;
use rslint_parser::{parse_text, AstNode, SyntaxNode, TextRange};
-use tracing::{error, trace};
+use tracing::trace;
use crate::{
analyzers, assists,
suppressions::{self, Suppressions},
- Analysis, AnalyzerContext, AnalyzerResult, AssistContext,
+ Analysis, AnalyzerContext, AssistContext,
};
-#[derive(Copy, Clone, Debug, Ord, PartialOrd, PartialEq, Eq, Hash)]
-pub struct FileId(pub usize);
+pub type FileId = usize;
#[derive(Default)]
pub struct AnalysisServer {
@@ -37,10 +35,10 @@ impl AnalysisServer {
let text = self
.get_file_text(file_id)
.expect("File contents missing while parsing");
- parse_text(&text, file_id.0).syntax()
+ parse_text(&text, file_id).syntax()
}
- pub fn suppressions(&self, file_id: FileId) -> Result<Suppressions> {
+ pub fn suppressions(&self, file_id: FileId) -> Suppressions {
let tree = self.parse(file_id);
suppressions::compute(tree)
}
@@ -57,43 +55,40 @@ impl AnalysisServer {
tree.covering_element(range).ancestors().find_map(T::cast)
}
- pub fn assists(&self, file_id: FileId, cursor_range: TextRange) -> AnalyzerResult {
+ pub fn assists(&self, file_id: FileId, cursor_range: TextRange) -> Analysis {
trace!("Assists range: {:?}", cursor_range);
- let a_ctx = AnalyzerContext::new(self, file_id);
- let ctx = AssistContext::new(&a_ctx, cursor_range);
let mut signals = vec![];
for provider in assists::all() {
+ let ctx = AssistContext::new(self, file_id, cursor_range, provider);
let analyze_fn = provider.analyze;
- if let Some(signal) = analyze_fn(&ctx) {
- signals.extend(signal.signals.into_iter())
+ if let Some(analysis) = analyze_fn(&ctx) {
+ signals.extend(analysis.signals.into_iter())
}
}
-
- Ok(signals.into())
+ signals.into()
}
- pub fn analyze(&self, file_id: FileId) -> AnalyzerResult {
- let ctx = AnalyzerContext::new(self, file_id);
- let suppressions = self.suppressions(file_id)?;
+ pub fn analyze(&self, file_id: FileId) -> Analysis {
+ let suppressions = self.suppressions(file_id);
let mut signals = vec![];
for analyzer in analyzers::all() {
+ let ctx = AnalyzerContext::new(self, file_id, analyzer);
let analyze_fn = analyzer.analyze;
- match analyze_fn(&ctx) {
- Ok(signal) => {
- for s in signal.signals {
- if s.is_diagnostic() && suppressions.match_range(analyzer.name, s.range()) {
+ if let Some(analysis) = analyze_fn(&ctx) {
+ for s in analysis.signals {
+ if let Some(range) = s.range() {
+ if s.is_diagnostic() && suppressions.match_range(analyzer.name, range) {
continue;
}
signals.push(s);
}
}
- Err(e) => error!("Err: {:?}", e),
}
}
- Ok(Analysis { signals })
+ signals.into()
}
}

MODIFY
crates/rome_analyze/src/analyzers.rs
crates/rome_analyze/src/analyzers.rs
@@ -4,9 +4,10 @@ pub(crate) mod use_single_case_statement;
pub(crate) mod use_while;
use once_cell::sync::Lazy;
+use rslint_errors::{Diagnostic, Span};
use rslint_parser::{AstNode, SyntaxNode, TextRange};
-use crate::{analysis_server::AnalysisServer, ActionCategory, AnalyzerResult, FileId};
+use crate::{analysis_server::AnalysisServer, ActionCategory, Analysis, FileId};
static ALL_ANALYZERS: Lazy<Vec<Analyzer>> = Lazy::new(|| {
vec![
@@ -20,33 +21,51 @@ static ALL_ANALYZERS: Lazy<Vec<Analyzer>> = Lazy::new(|| {
pub struct Analyzer {
pub name: &'static str,
pub action_categories: Vec<ActionCategory>,
- pub(crate) analyze: fn(&AnalyzerContext) -> AnalyzerResult,
+ pub(crate) analyze: fn(&AnalyzerContext) -> Option<Analysis>,
}
pub struct AnalyzerContext<'a> {
pub file_id: FileId,
analysis_server: &'a AnalysisServer,
+ analyzer: &'a Analyzer,
}
impl<'a> AnalyzerContext<'a> {
- pub fn new(analysis_server: &'a AnalysisServer, file_id: FileId) -> Self {
+ pub(crate) fn new(
+ analysis_server: &'a AnalysisServer,
+ file_id: FileId,
+ analyzer: &'a Analyzer,
+ ) -> Self {
Self {
analysis_server,
file_id,
+ analyzer,
}
}
+ /// Get the root [SyntaxNode] for the file being analyzed
pub fn tree(&self) -> SyntaxNode {
self.analysis_server.parse(self.file_id)
}
+ /// Iterate over syntax nodes in this file that can be cast to T
pub fn query_nodes<T: AstNode>(&self) -> impl Iterator<Item = T> {
self.analysis_server.query_nodes(self.file_id)
}
+ /// Find the deepest AST node of type T that covers a TextRange
pub fn find_node_at_range<T: AstNode>(&self, range: TextRange) -> Option<T> {
self.analysis_server.find_node_at_range(self.file_id, range)
}
+
+ /// Create an error [Diagnostic] which includes the [FileId] of the file being analyzed
+ /// and the analyzer's name as a "code". The provided [Span] is recorded as the primary
+ /// label for the diagnostic.
+ #[must_use]
+ pub fn error(&self, span: impl Span, message: impl Into<String>) -> Diagnostic {
+ let code = self.analyzer.name;
+ Diagnostic::error(self.file_id, code, message.into()).primary(span, "")
+ }
}
pub fn all() -> impl Iterator<Item = &'static Analyzer> {

MODIFY
crates/rome_analyze/src/analyzers/no_double_equals.rs
crates/rome_analyze/src/analyzers/no_double_equals.rs
-use anyhow::Result;
use rslint_parser::{
ast::{self, JsAnyExpression},
AstNode,
@@ -6,17 +5,17 @@ use rslint_parser::{
SyntaxResult,
};
-use crate::{ActionCategory, Analysis, Analyzer, AnalyzerContext, Signal};
+use crate::{signals::DiagnosticExt, Analysis, Analyzer, AnalyzerContext};
pub fn create() -> Analyzer {
Analyzer {
name: "noDoubleEquals",
- action_categories: vec![ActionCategory::SafeFix],
+ action_categories: vec![],
analyze,
}
}
-fn analyze(ctx: &AnalyzerContext) -> Result<Analysis> {
+fn analyze(ctx: &AnalyzerContext) -> Option<Analysis> {
ctx.query_nodes::<ast::JsBinaryExpression>()
.filter_map(|n| {
let op = n.operator().ok()?;
@@ -30,8 +29,8 @@ fn analyze(ctx: &AnalyzerContext) -> Result<Analysis> {
return None;
}
- let message = format!("rome: do not use the {} operator", op.text_trimmed());
- let signal = Signal::diagnostic(op, message);
+ let message = format!("Do not use the {} operator", op.text_trimmed());
+ let signal = ctx.error(op, message).into_signal();
Some(signal)
})
.collect()

MODIFY
crates/rome_analyze/src/analyzers/no_var.rs
crates/rome_analyze/src/analyzers/no_var.rs
-use anyhow::Result;
-use rslint_parser::{ast, JsSyntaxKind};
+use rslint_parser::{ast, AstNode};
-use crate::{Analysis, Analyzer, AnalyzerContext, Signal};
+use crate::{signals::DiagnosticExt, Analysis, Analyzer, AnalyzerContext};
pub fn create() -> Analyzer {
Analyzer {
@@ -11,9 +10,9 @@ pub fn create() -> Analyzer {
}
}
-fn analyze(ctx: &AnalyzerContext) -> Result<Analysis> {
+fn analyze(ctx: &AnalyzerContext) -> Option<Analysis> {
ctx.query_nodes::<ast::JsVariableDeclarations>()
- .filter(|n| n.kind().map(|k| k.kind()) == Ok(JsSyntaxKind::VAR_KW))
- .map(|n| Signal::diagnostic(n, "rome: do not use var"))
+ .filter(|n| n.is_var())
+ .map(|n| ctx.error(n.range(), "Do not use var").into_signal())
.collect()
}

MODIFY
crates/rome_analyze/src/analyzers/use_single_case_statement.rs
crates/rome_analyze/src/analyzers/use_single_case_statement.rs
-use anyhow::Result;
-use rslint_parser::{ast, AstNodeList};
+use rslint_parser::{ast, AstNode, AstNodeList};
-use crate::{ActionCategory, Analysis, Analyzer, AnalyzerContext, Signal};
+use crate::{signals::DiagnosticExt, Analysis, Analyzer, AnalyzerContext};
pub fn create() -> Analyzer {
Analyzer {
name: "useSingleCaseStatement",
- action_categories: vec![ActionCategory::Suggestion],
+ action_categories: vec![],
analyze,
}
}
-fn analyze(ctx: &AnalyzerContext) -> Result<Analysis> {
+fn analyze(ctx: &AnalyzerContext) -> Option<Analysis> {
ctx.query_nodes::<ast::JsCaseClause>()
.filter(|n| n.consequent().len() > 1)
.map(|node| {
- let message = "rome: A switch case should only have a single statement.";
- Signal::diagnostic(node, message)
+ let message = "A switch case should only have a single statement.";
+ ctx.error(node.range(), message).into_signal()
})
.collect()
}

MODIFY
crates/rome_analyze/src/analyzers/use_while.rs
crates/rome_analyze/src/analyzers/use_while.rs
-use anyhow::Result;
-use rslint_parser::ast;
+use rslint_parser::{ast, AstNode};
-use crate::{ActionCategory, Analysis, Analyzer, AnalyzerContext, Signal};
+use crate::{signals::DiagnosticExt, Analysis, Analyzer, AnalyzerContext};
pub fn create() -> Analyzer {
Analyzer {
name: "useWhile",
- action_categories: vec![ActionCategory::Suggestion],
+ action_categories: vec![],
analyze,
}
}
-fn analyze(ctx: &AnalyzerContext) -> Result<Analysis> {
+fn analyze(ctx: &AnalyzerContext) -> Option<Analysis> {
ctx.query_nodes::<ast::JsForStatement>()
.filter(|n| n.initializer().is_none() && n.update().is_none())
.map(|node| {
- let message = "rome: Use a while loop instead of a for loop";
- Signal::diagnostic(node, message)
+ let message = "Use a while loop instead of a for loop";
+ ctx.error(node.range(), message).into_signal()
})
.collect()
}

MODIFY
crates/rome_analyze/src/assists.rs
crates/rome_analyze/src/assists.rs
@@ -5,7 +5,7 @@ pub mod flip_bin_exp;
use once_cell::sync::Lazy;
use rslint_parser::{AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset};
-use crate::{ActionCategory, Analysis, AnalyzerContext, FileId};
+use crate::{ActionCategory, Analysis, AnalysisServer, AnalyzerContext, FileId};
static ALL_ASSIST_PROVIDERS: Lazy<Vec<AssistProvider>> = Lazy::new(|| vec![flip_bin_exp::create()]);
@@ -22,36 +22,54 @@ pub struct AssistContext<'a> {
file_id: FileId,
cursor_range: TextRange,
offset: TextSize,
- analyzer_context: &'a AnalyzerContext<'a>,
+ analysis_server: &'a AnalysisServer,
+ assist_provider: &'a AssistProvider,
}
impl<'a> AssistContext<'a> {
- pub(crate) fn new(analyzer_context: &'a AnalyzerContext, cursor_range: TextRange) -> Self {
+ pub(crate) fn new(
+ analysis_server: &'a AnalysisServer,
+ file_id: FileId,
+ cursor_range: TextRange,
+ assist_provider: &'a AssistProvider,
+ ) -> Self {
let offset = cursor_range.start();
Self {
cursor_range,
offset,
- analyzer_context,
- file_id: analyzer_context.file_id,
+ analysis_server,
+ file_id,
+ assist_provider,
}
}
+ /// Get the cursor_range for this AssistContext
pub(crate) fn range(&self) -> TextRange {
self.cursor_range
}
- pub(crate) fn tree(&self) -> SyntaxNode {
- self.analyzer_context.tree()
+ /// Get the root [SyntaxNode] for the file being analyzed
+ pub fn tree(&self) -> SyntaxNode {
+ self.analysis_server.parse(self.file_id)
+ }
+
+ /// Iterate over syntax nodes in the file being analyzed that can be cast to T
+ pub fn query_nodes<T: AstNode>(&self) -> impl Iterator<Item = T> {
+ self.analysis_server.query_nodes(self.file_id)
}
- pub(crate) fn query_nodes<T: AstNode>(&self) -> impl Iterator<Item = T> {
- self.analyzer_context.query_nodes()
+ /// Find the deepest AST node of type T that covers a TextRange
+ pub fn find_node_at_range<T: AstNode>(&self, range: TextRange) -> Option<T> {
+ self.analysis_server.find_node_at_range(self.file_id, range)
}
- pub(crate) fn find_node_at_cursor_range<T: AstNode>(&self) -> Option<T> {
- self.analyzer_context.find_node_at_range(self.cursor_range)
+ /// Find the deepest AST node of type T that covers this AssistContext's cursor_range
+ pub fn find_node_at_cursor_range<T: AstNode>(&self) -> Option<T> {
+ self.analysis_server
+ .find_node_at_range(self.file_id, self.cursor_range)
}
+ /// Find the token that covers the start of the AssistContext's cursor_range
pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
self.tree().token_at_offset(self.offset)
}

MODIFY
crates/rome_analyze/src/lib.rs
crates/rome_analyze/src/lib.rs
@@ -10,5 +10,5 @@ pub use analysis_server::{AnalysisServer, FileId};
pub use analyzers::{Analyzer, AnalyzerContext};
pub use assists::AssistContext;
pub use categories::ActionCategory;
-pub use signals::{Action, Analysis, AnalyzerResult, Signal, TextAction};
+pub use signals::{Action, Analysis, DiagnosticExt, Signal, TextAction};
pub use syntax_edit::{Indel, SyntaxEdit};

MODIFY
crates/rome_analyze/src/signals.rs
crates/rome_analyze/src/signals.rs
-use anyhow::Result;
-use rslint_parser::{SyntaxElement, TextRange};
+use rslint_errors::{Diagnostic, Span};
+use rslint_parser::TextRange;
use crate::{ActionCategory, Indel, SyntaxEdit};
-pub type AnalyzerResult = Result<Analysis>;
-
#[derive(Debug)]
pub enum Signal {
Diagnostic(AnalyzeDiagnostic),
Action(Action),
}
-impl Signal {
- pub fn diagnostic(target: impl Into<SyntaxElement>, message: impl Into<String>) -> Self {
- let range = target.into().text_trimmed_range();
- let diag = AnalyzeDiagnostic {
- range,
- message: message.into(),
- actions: Vec::new(),
- };
- diag.into()
- }
-
- pub fn diagnostic_with_replacement(
- target: impl Into<SyntaxElement>,
- message: impl Into<String>,
- action_title: impl Into<String>,
- replacement: impl Into<SyntaxElement>,
- category: ActionCategory,
- ) -> Self {
- let target: SyntaxElement = target.into();
- let range = target.text_trimmed_range();
- let edit = SyntaxEdit::Replace {
- target,
- replacement: replacement.into(),
- trimmed: true,
- };
- let action = Action {
- title: action_title.into(),
- range,
- edits: vec![edit],
- category,
- };
- let diag = AnalyzeDiagnostic {
- range,
- message: message.into(),
- actions: vec![action],
- };
- diag.into()
- }
-}
-
impl Signal {
pub fn is_diagnostic(&self) -> bool {
matches!(self, Signal::Diagnostic(_))
@@ -60,28 +18,30 @@ impl Signal {
matches!(self, Signal::Action(_))
}
- pub fn range(&self) -> TextRange {
+ /// For an [Action], returns the valid range.
+ /// For a [Diagnostic], returns the text range of the primary label.
+ pub fn range(&self) -> Option<TextRange> {
match self {
- Signal::Diagnostic(it) => it.range,
- Signal::Action(it) => it.range,
+ Signal::Diagnostic(it) => it.range(),
+ Signal::Action(it) => Some(it.range),
}
}
}
-#[derive(Debug)]
-pub struct DiagnosticWithActions {
- pub diagnostic: AnalyzeDiagnostic,
- pub actions: Vec<Action>,
+impl From<Diagnostic> for Signal {
+ fn from(d: Diagnostic) -> Self {
+ Self::Diagnostic(d.into())
+ }
}
-impl From<AnalyzeDiagnostic> for Signal {
- fn from(d: AnalyzeDiagnostic) -> Self {
- Self::Diagnostic(d)
+impl From<Diagnostic> for AnalyzeDiagnostic {
+ fn from(d: Diagnostic) -> Self {
+ AnalyzeDiagnostic::new(d)
}
}
-impl From<AnalyzeDiagnostic> for Analysis {
- fn from(d: AnalyzeDiagnostic) -> Self {
+impl From<Diagnostic> for Analysis {
+ fn from(d: Diagnostic) -> Self {
Analysis {
signals: vec![d.into()],
}
@@ -102,22 +62,64 @@ impl From<Action> for Analysis {
}
}
-impl FromIterator<Signal> for Result<Analysis> {
+impl FromIterator<Signal> for Option<Analysis> {
fn from_iter<T: IntoIterator<Item = Signal>>(iter: T) -> Self {
let analysis = Analysis {
signals: Vec::from_iter(iter),
};
- Ok(analysis)
+ Some(analysis)
+ }
+}
+
+impl FromIterator<Signal> for Analysis {
+ fn from_iter<T: IntoIterator<Item = Signal>>(iter: T) -> Self {
+ Analysis {
+ signals: Vec::from_iter(iter),
+ }
+ }
+}
+
+impl FromIterator<AnalyzeDiagnostic> for Analysis {
+ fn from_iter<T: IntoIterator<Item = AnalyzeDiagnostic>>(iter: T) -> Self {
+ Analysis {
+ signals: iter.into_iter().map(Signal::Diagnostic).collect(),
+ }
}
}
#[derive(Debug, Clone)]
+/// Combines an rslint_errors Diagnostic with [SyntaxEdit] actions.
+///
+/// The suggestions on a [rslint_errors::Diagnostic] are only suitable for text edits.
+/// Perhaps that diagnostic type can be modified so that this type is
+/// unnecessary, but we may not want the core diagnostics format to directly
+/// reference syntax nodes.
pub struct AnalyzeDiagnostic {
- pub range: TextRange,
- pub message: String,
+ pub diagnostic: Diagnostic,
pub actions: Vec<Action>,
}
+impl AnalyzeDiagnostic {
+ pub fn new(diagnostic: Diagnostic) -> Self {
+ Self {
+ diagnostic,
+ actions: vec![],
+ }
+ }
+
+ pub fn with_actions(diagnostic: Diagnostic, actions: Vec<Action>) -> Self {
+ Self {
+ diagnostic,
+ actions,
+ }
+ }
+
+ /// Get the [TextRange] corresponding to the primary label of this diagnostic.
+ pub fn range(&self) -> Option<TextRange> {
+ self.diagnostic.primary_text_range()
+ }
+}
+
#[derive(Debug, Clone)]
pub struct Action {
pub title: String,
@@ -145,6 +147,7 @@ impl From<Action> for TextAction {
}
}
+// TODO: Errors produced by analyzers should be collected on Analysis
#[derive(Default, Debug)]
pub struct Analysis {
pub signals: Vec<Signal>,
@@ -168,3 +171,23 @@ impl From<Vec<Signal>> for Analysis {
Self { signals }
}
}
+
+/// An extension trait for [rslint_errors::Diagnostic]
+/// In the future, the Diagnostic format might be modified directly.
+pub trait DiagnosticExt {
+ fn into_signal(self) -> Signal;
+
+ fn primary_text_range(&self) -> Option<TextRange>;
+}
+
+impl DiagnosticExt for Diagnostic {
+ /// Convenience method to wrap a [Diagnostic] in [Signal::Diagnostic]
+ fn into_signal(self) -> Signal {
+ Signal::Diagnostic(self.into())
+ }
+
+ /// Get the [TextRange] of the diagnostic's primary label
+ fn primary_text_range(&self) -> Option<TextRange> {
+ self.primary.as_ref().map(|p| p.span.range.as_text_range())
+ }
+}

MODIFY
crates/rome_analyze/src/suppressions.rs
crates/rome_analyze/src/suppressions.rs
use std::collections::HashMap;
-use anyhow::Result;
use rslint_parser::{SyntaxNode, SyntaxToken, TextRange};
const ROME_IGNORE: &str = "rome-ignore";
@@ -28,7 +27,7 @@ impl Suppressions {
}
}
-pub fn compute(node: SyntaxNode) -> Result<Suppressions> {
+pub fn compute(node: SyntaxNode) -> Suppressions {
let mut suppressions: HashMap<String, Vec<TextRange>> = HashMap::new();
for token in node.descendants_tokens() {
@@ -55,7 +54,7 @@ pub fn compute(node: SyntaxNode) -> Result<Suppressions> {
}
}
}
- Ok(Suppressions { suppressions })
+ Suppressions { suppressions }
}
// TODO: Improve this logic

MODIFY
crates/rome_lsp/Cargo.toml
crates/rome_lsp/Cargo.toml
@@ -12,6 +12,7 @@ anyhow = "1.0.52"
indexmap = "1.8.0"
rome_formatter = { path = "../rome_formatter" }
rome_analyze = { path = "../rome_analyze" }
+rslint_errors = { path = "../rslint_errors", version = "0.2.0" }
rslint_parser = { path = "../rslint_parser" }
lspower = "1.5.0"
tokio = { version = "1.15.0", features = ["full" ] }

MODIFY
crates/rome_lsp/src/handlers.rs
crates/rome_lsp/src/handlers.rs
@@ -7,7 +7,7 @@ use rslint_parser::parse_text;
use crate::line_index;
pub fn format(text: &str, file_id: FileId) -> Result<Vec<TextEdit>> {
- let tree = parse_text(text, file_id.0).syntax();
+ let tree = parse_text(text, file_id).syntax();
let options = FormatOptions {
indent_style: IndentStyle::Tab,

MODIFY
crates/rome_lsp/src/server.rs
crates/rome_lsp/src/server.rs
@@ -122,14 +122,15 @@ async fn analyze_file(
let mut actions: Vec<TextAction> = vec![];
let mut diagnostics: Vec<Diagnostic> = vec![];
- let analysis = analysis_server.analyze(file_id)?;
+ let analysis = analysis_server.analyze(file_id);
for signal in analysis.signals {
match signal {
Signal::Diagnostic(d) => {
- let range = crate::utils::range(&line_index, d.range);
- diagnostics.push(Diagnostic::new_simple(range, d.message));
+ if let Some(diagnostic) = utils::diagnostic_to_lsp(d.diagnostic, &line_index) {
+ diagnostics.push(diagnostic);
actions.extend(d.actions.into_iter().map(TextAction::from));
}
+ }
Signal::Action(a) => actions.push(a.into()),
}
}
@@ -150,7 +151,6 @@ fn compute_actions(
let code_actions: Vec<CodeAction> = analysis_api
.assists(file_id, cursor_range)
- .unwrap()
.into_actions()
.map(|a| utils::text_action_to_lsp(&a.into(), &line_index, uri.to_owned(), None))
.collect();

MODIFY
crates/rome_lsp/src/url_interner.rs
crates/rome_lsp/src/url_interner.rs
@@ -18,7 +18,7 @@ impl UrlInterner {
/// If `path` does not exists in `self`, returns [`None`].
#[allow(unused)]
pub(crate) fn get(&self, path: &Url) -> Option<FileId> {
- self.map.get_index_of(path).map(FileId)
+ self.map.get_index_of(path)
}
/// Insert `path` in `self`.
@@ -28,7 +28,7 @@ impl UrlInterner {
pub(crate) fn intern(&mut self, path: Url) -> FileId {
let (id, _added) = self.map.insert_full(path);
assert!(id < usize::MAX);
- FileId(id)
+ id
}
/// Returns the path corresponding to `id`.
@@ -38,6 +38,6 @@ impl UrlInterner {
/// Panics if `id` does not exists in `self`.
#[allow(dead_code)]
pub(crate) fn lookup(&self, id: FileId) -> &Url {
- self.map.get_index(id.0 as usize).unwrap()
+ self.map.get_index(id).unwrap()
}
}

MODIFY
crates/rome_lsp/src/utils.rs
crates/rome_lsp/src/utils.rs
@@ -2,7 +2,7 @@ use std::collections::HashMap;
use crate::line_index::{LineCol, LineIndex};
use lspower::lsp::{self, CodeAction, CodeActionKind, Diagnostic, TextEdit, Url, WorkspaceEdit};
-use rome_analyze::{Indel, TextAction};
+use rome_analyze::{DiagnosticExt, Indel, TextAction};
use tracing::trace;
use rslint_parser::{TextRange, TextSize};
@@ -72,3 +72,20 @@ pub(crate) fn text_action_to_lsp(
data: None,
}
}
+
+/// Convert an [rslint_errors::Diagnostic] to a [lsp::Diagnostic], using the span
+/// of the diagnostic's primary label as the diagnostic range.
+/// Requires a [LineIndex] to convert a byte offset range to the line/col range
+/// expected by LSP.
+pub(crate) fn diagnostic_to_lsp(
+ diagnostic: rslint_errors::Diagnostic,
+ line_index: &LineIndex,
+) -> Option<lsp::Diagnostic> {
+ let text_range = diagnostic.primary_text_range()?;
+ let lsp_range = crate::utils::range(line_index, text_range);
+ let message = diagnostic.title;
+ let code = diagnostic.code.map(lspower::lsp::NumberOrString::String);
+ let source = Some("rome".into());
+ let diagnostic = Diagnostic::new(lsp_range, None, code, source, message, None, None);
+ Some(diagnostic)
+}

feat: use rslint_errors Diagnostic in rome_analyze (#1940)