mirror of https://github.com/helix-editor/helix
LSP commands: Move offset encoding onto the Location type
<https://github.com/helix-editor/helix/pull/11486> introduced a Location type in the LSP commands module which unified helpers like `jump_to_location`. This change moves `OffsetEncoding` onto that type. `SymbolInformationItem` and `PickerDiagnostic` already had fields for carrying the offset encoding. We would want a similar setup for goto definition/references as well (for supporting multiple language servers with that feature) but those use the `Location` type. By moving `OffsetEncoding` onto `Location` we make future changes to allow mulitple language servers possible for goto definition/references features and also simplify some calls for symbols and diagnostics.pull/12731/head
parent
c9dc940428
commit
ba116b47a0
|
@ -61,14 +61,19 @@ macro_rules! language_server_with_feature {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A wrapper around `lsp::Location` that swaps out the LSP URI for `helix_core::Uri`.
|
/// A wrapper around `lsp::Location` that swaps out the LSP URI for `helix_core::Uri` and adds
|
||||||
|
/// the server's offset encoding.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
struct Location {
|
struct Location {
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
range: lsp::Range,
|
range: lsp::Range,
|
||||||
|
offset_encoding: OffsetEncoding,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lsp_location_to_location(location: lsp::Location) -> Option<Location> {
|
fn lsp_location_to_location(
|
||||||
|
location: lsp::Location,
|
||||||
|
offset_encoding: OffsetEncoding,
|
||||||
|
) -> Option<Location> {
|
||||||
let uri = match location.uri.try_into() {
|
let uri = match location.uri.try_into() {
|
||||||
Ok(uri) => uri,
|
Ok(uri) => uri,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -79,13 +84,13 @@ fn lsp_location_to_location(location: lsp::Location) -> Option<Location> {
|
||||||
Some(Location {
|
Some(Location {
|
||||||
uri,
|
uri,
|
||||||
range: location.range,
|
range: location.range,
|
||||||
|
offset_encoding,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SymbolInformationItem {
|
struct SymbolInformationItem {
|
||||||
location: Location,
|
location: Location,
|
||||||
symbol: lsp::SymbolInformation,
|
symbol: lsp::SymbolInformation,
|
||||||
offset_encoding: OffsetEncoding,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DiagnosticStyles {
|
struct DiagnosticStyles {
|
||||||
|
@ -98,7 +103,6 @@ struct DiagnosticStyles {
|
||||||
struct PickerDiagnostic {
|
struct PickerDiagnostic {
|
||||||
location: Location,
|
location: Location,
|
||||||
diag: lsp::Diagnostic,
|
diag: lsp::Diagnostic,
|
||||||
offset_encoding: OffsetEncoding,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn location_to_file_location(location: &Location) -> Option<FileLocation> {
|
fn location_to_file_location(location: &Location) -> Option<FileLocation> {
|
||||||
|
@ -110,12 +114,7 @@ fn location_to_file_location(location: &Location) -> Option<FileLocation> {
|
||||||
Some((path.into(), line))
|
Some((path.into(), line))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn jump_to_location(
|
fn jump_to_location(editor: &mut Editor, location: &Location, action: Action) {
|
||||||
editor: &mut Editor,
|
|
||||||
location: &Location,
|
|
||||||
offset_encoding: OffsetEncoding,
|
|
||||||
action: Action,
|
|
||||||
) {
|
|
||||||
let (view, doc) = current!(editor);
|
let (view, doc) = current!(editor);
|
||||||
push_jump(view, doc);
|
push_jump(view, doc);
|
||||||
|
|
||||||
|
@ -124,7 +123,13 @@ fn jump_to_location(
|
||||||
editor.set_error(err);
|
editor.set_error(err);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
jump_to_position(editor, path, location.range, offset_encoding, action);
|
jump_to_position(
|
||||||
|
editor,
|
||||||
|
path,
|
||||||
|
location.range,
|
||||||
|
location.offset_encoding,
|
||||||
|
action,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn jump_to_position(
|
fn jump_to_position(
|
||||||
|
@ -220,9 +225,9 @@ fn diag_picker(
|
||||||
location: Location {
|
location: Location {
|
||||||
uri: uri.clone(),
|
uri: uri.clone(),
|
||||||
range: diag.range,
|
range: diag.range,
|
||||||
|
offset_encoding: ls.offset_encoding(),
|
||||||
},
|
},
|
||||||
diag,
|
diag,
|
||||||
offset_encoding: ls.offset_encoding(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,7 +291,7 @@ fn diag_picker(
|
||||||
flat_diag,
|
flat_diag,
|
||||||
styles,
|
styles,
|
||||||
move |cx, diag, action| {
|
move |cx, diag, action| {
|
||||||
jump_to_location(cx.editor, &diag.location, diag.offset_encoding, action);
|
jump_to_location(cx.editor, &diag.location, action);
|
||||||
let (view, doc) = current!(cx.editor);
|
let (view, doc) = current!(cx.editor);
|
||||||
view.diagnostics_handler
|
view.diagnostics_handler
|
||||||
.immediately_show_diagnostic(doc, view.id);
|
.immediately_show_diagnostic(doc, view.id);
|
||||||
|
@ -314,10 +319,10 @@ pub fn symbol_picker(cx: &mut Context) {
|
||||||
location: lsp::Location::new(file.uri.clone(), symbol.selection_range),
|
location: lsp::Location::new(file.uri.clone(), symbol.selection_range),
|
||||||
container_name: None,
|
container_name: None,
|
||||||
},
|
},
|
||||||
offset_encoding,
|
|
||||||
location: Location {
|
location: Location {
|
||||||
uri: uri.clone(),
|
uri: uri.clone(),
|
||||||
range: symbol.selection_range,
|
range: symbol.selection_range,
|
||||||
|
offset_encoding,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
for child in symbol.children.into_iter().flatten() {
|
for child in symbol.children.into_iter().flatten() {
|
||||||
|
@ -355,9 +360,9 @@ pub fn symbol_picker(cx: &mut Context) {
|
||||||
location: Location {
|
location: Location {
|
||||||
uri: doc_uri.clone(),
|
uri: doc_uri.clone(),
|
||||||
range: symbol.location.range,
|
range: symbol.location.range,
|
||||||
|
offset_encoding,
|
||||||
},
|
},
|
||||||
symbol,
|
symbol,
|
||||||
offset_encoding,
|
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
lsp::DocumentSymbolResponse::Nested(symbols) => {
|
lsp::DocumentSymbolResponse::Nested(symbols) => {
|
||||||
|
@ -410,7 +415,7 @@ pub fn symbol_picker(cx: &mut Context) {
|
||||||
symbols,
|
symbols,
|
||||||
(),
|
(),
|
||||||
move |cx, item, action| {
|
move |cx, item, action| {
|
||||||
jump_to_location(cx.editor, &item.location, item.offset_encoding, action);
|
jump_to_location(cx.editor, &item.location, action);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.with_preview(move |_editor, item| location_to_file_location(&item.location))
|
.with_preview(move |_editor, item| location_to_file_location(&item.location))
|
||||||
|
@ -467,9 +472,9 @@ pub fn workspace_symbol_picker(cx: &mut Context) {
|
||||||
location: Location {
|
location: Location {
|
||||||
uri,
|
uri,
|
||||||
range: symbol.location.range,
|
range: symbol.location.range,
|
||||||
|
offset_encoding,
|
||||||
},
|
},
|
||||||
symbol,
|
symbol,
|
||||||
offset_encoding,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -521,7 +526,7 @@ pub fn workspace_symbol_picker(cx: &mut Context) {
|
||||||
[],
|
[],
|
||||||
(),
|
(),
|
||||||
move |cx, item, action| {
|
move |cx, item, action| {
|
||||||
jump_to_location(cx.editor, &item.location, item.offset_encoding, action);
|
jump_to_location(cx.editor, &item.location, action);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.with_preview(|_editor, item| location_to_file_location(&item.location))
|
.with_preview(|_editor, item| location_to_file_location(&item.location))
|
||||||
|
@ -853,17 +858,12 @@ impl Display for ApplyEditErrorKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Precondition: `locations` should be non-empty.
|
/// Precondition: `locations` should be non-empty.
|
||||||
fn goto_impl(
|
fn goto_impl(editor: &mut Editor, compositor: &mut Compositor, locations: Vec<Location>) {
|
||||||
editor: &mut Editor,
|
|
||||||
compositor: &mut Compositor,
|
|
||||||
locations: Vec<Location>,
|
|
||||||
offset_encoding: OffsetEncoding,
|
|
||||||
) {
|
|
||||||
let cwdir = helix_stdx::env::current_working_dir();
|
let cwdir = helix_stdx::env::current_working_dir();
|
||||||
|
|
||||||
match locations.as_slice() {
|
match locations.as_slice() {
|
||||||
[location] => {
|
[location] => {
|
||||||
jump_to_location(editor, location, offset_encoding, Action::Replace);
|
jump_to_location(editor, location, Action::Replace);
|
||||||
}
|
}
|
||||||
[] => unreachable!("`locations` should be non-empty for `goto_impl`"),
|
[] => unreachable!("`locations` should be non-empty for `goto_impl`"),
|
||||||
_locations => {
|
_locations => {
|
||||||
|
@ -880,30 +880,35 @@ fn goto_impl(
|
||||||
},
|
},
|
||||||
)];
|
)];
|
||||||
|
|
||||||
let picker = Picker::new(columns, 0, locations, cwdir, move |cx, location, action| {
|
let picker = Picker::new(columns, 0, locations, cwdir, |cx, location, action| {
|
||||||
jump_to_location(cx.editor, location, offset_encoding, action)
|
jump_to_location(cx.editor, location, action)
|
||||||
})
|
})
|
||||||
.with_preview(move |_editor, location| location_to_file_location(location));
|
.with_preview(|_editor, location| location_to_file_location(location));
|
||||||
compositor.push(Box::new(overlaid(picker)));
|
compositor.push(Box::new(overlaid(picker)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_locations(definitions: Option<lsp::GotoDefinitionResponse>) -> Vec<Location> {
|
fn to_locations(
|
||||||
|
definitions: Option<lsp::GotoDefinitionResponse>,
|
||||||
|
offset_encoding: OffsetEncoding,
|
||||||
|
) -> Vec<Location> {
|
||||||
match definitions {
|
match definitions {
|
||||||
Some(lsp::GotoDefinitionResponse::Scalar(location)) => {
|
Some(lsp::GotoDefinitionResponse::Scalar(location)) => {
|
||||||
lsp_location_to_location(location).into_iter().collect()
|
lsp_location_to_location(location, offset_encoding)
|
||||||
|
.into_iter()
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
Some(lsp::GotoDefinitionResponse::Array(locations)) => locations
|
Some(lsp::GotoDefinitionResponse::Array(locations)) => locations
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(lsp_location_to_location)
|
.flat_map(|location| lsp_location_to_location(location, offset_encoding))
|
||||||
.collect(),
|
.collect(),
|
||||||
Some(lsp::GotoDefinitionResponse::Link(locations)) => locations
|
Some(lsp::GotoDefinitionResponse::Link(locations)) => locations
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|location_link| {
|
.map(|location_link| {
|
||||||
lsp::Location::new(location_link.target_uri, location_link.target_range)
|
lsp::Location::new(location_link.target_uri, location_link.target_range)
|
||||||
})
|
})
|
||||||
.flat_map(lsp_location_to_location)
|
.flat_map(|location| lsp_location_to_location(location, offset_encoding))
|
||||||
.collect(),
|
.collect(),
|
||||||
None => Vec::new(),
|
None => Vec::new(),
|
||||||
}
|
}
|
||||||
|
@ -924,11 +929,11 @@ where
|
||||||
cx.callback(
|
cx.callback(
|
||||||
future,
|
future,
|
||||||
move |editor, compositor, response: Option<lsp::GotoDefinitionResponse>| {
|
move |editor, compositor, response: Option<lsp::GotoDefinitionResponse>| {
|
||||||
let items = to_locations(response);
|
let items = to_locations(response, offset_encoding);
|
||||||
if items.is_empty() {
|
if items.is_empty() {
|
||||||
editor.set_error("No definition found.");
|
editor.set_error("No definition found.");
|
||||||
} else {
|
} else {
|
||||||
goto_impl(editor, compositor, items, offset_encoding);
|
goto_impl(editor, compositor, items);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -991,12 +996,12 @@ pub fn goto_reference(cx: &mut Context) {
|
||||||
let items: Vec<Location> = response
|
let items: Vec<Location> = response
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.flat_map(lsp_location_to_location)
|
.flat_map(|location| lsp_location_to_location(location, offset_encoding))
|
||||||
.collect();
|
.collect();
|
||||||
if items.is_empty() {
|
if items.is_empty() {
|
||||||
editor.set_error("No references found.");
|
editor.set_error("No references found.");
|
||||||
} else {
|
} else {
|
||||||
goto_impl(editor, compositor, items, offset_encoding);
|
goto_impl(editor, compositor, items);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue