Skip to content

Commit

Permalink
feat: definitions and references lsp hint (#1016)
Browse files Browse the repository at this point in the history
Add goto definition support for LSP consumers. Adding support for it
directly in LSP server could *maybe* be doable, but unreliable (e.g.
templates are complicating things) and so a lot of parser logic would
need to be reimplemented for all edge cases, so I think this is better
added directly to kanata-parser.
  • Loading branch information
rszyma committed Jun 7, 2024
1 parent 849e5d7 commit 306e172
Show file tree
Hide file tree
Showing 11 changed files with 330 additions and 138 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ jobs:
- name: Run clippy simulated output
run: cargo clippy --all --features=simulated_output,cmd -- -D warnings

- name: Run clippy for parser with lsp feature
run: cargo clippy -p kanata-parser --features=lsp -- -D warnings

build-test-clippy-windows:
runs-on: ${{ matrix.os }}
strategy:
Expand Down
1 change: 1 addition & 0 deletions parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ bytemuck = "1.15.0"
cmd = []
interception_driver = []
gui = []
lsp = []
9 changes: 0 additions & 9 deletions parser/src/cfg/defcfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,23 @@ use crate::{anyhow_expr, anyhow_span, bail, bail_expr, bail_span};
pub struct CfgOptionsGui {
/// File name / path to the tray icon file.
pub tray_icon: Option<String>,
#[cfg(all(target_os = "windows", feature = "gui"))]
/// Whether to match layer names to icon files without an explicit 'icon' field
pub icon_match_layer_name: bool,
/// Show tooltip on layer changes showing layer icons
#[cfg(all(target_os = "windows", feature = "gui"))]
pub tooltip_layer_changes: bool,
/// Show tooltip on layer changes for the default/base layer
#[cfg(all(target_os = "windows", feature = "gui"))]
pub tooltip_no_base: bool,
/// Show tooltip on layer changes even for layers without an icon
#[cfg(all(target_os = "windows", feature = "gui"))]
pub tooltip_show_blank: bool,
/// Show tooltip on layer changes for this duration (ms)
#[cfg(all(target_os = "windows", feature = "gui"))]
pub tooltip_duration: u16,
/// Show system notification message on config reload
#[cfg(all(target_os = "windows", feature = "gui"))]
pub notify_cfg_reload: bool,
/// Disable sound for the system notification message on config reload
#[cfg(all(target_os = "windows", feature = "gui"))]
pub notify_cfg_reload_silent: bool,
/// Show system notification message on errors
#[cfg(all(target_os = "windows", feature = "gui"))]
pub notify_error: bool,
/// Set tooltip size (width, height)
#[cfg(all(target_os = "windows", feature = "gui"))]
pub tooltip_size: (u16, u16),
}
#[cfg(all(any(target_os = "windows", target_os = "unknown"), feature = "gui"))]
Expand Down
33 changes: 23 additions & 10 deletions parser/src/cfg/deftemplate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,15 @@ struct Template {
///
/// Syntax of `deftemplate` is:
///
/// `(deftemplate (<list of template vars>) <rest of template>)`
/// `(deftemplate <template name> (<list of template vars>) <rest of template>)`
///
/// Syntax of `template-expand` is:
///
/// `(template-expand <template name> <template var substitutions>)`
pub fn expand_templates(mut toplevel_exprs: Vec<TopLevel>) -> Result<Vec<TopLevel>> {
pub fn expand_templates(
mut toplevel_exprs: Vec<TopLevel>,
lsp_hints: &mut LspHints,
) -> Result<Vec<TopLevel>> {
let mut templates: Vec<Template> = vec![];

// Find defined templates
Expand All @@ -55,7 +58,7 @@ pub fn expand_templates(mut toplevel_exprs: Vec<TopLevel>) -> Result<Vec<TopLeve
}

// Parse template name
let name = list
let (name, _name_span) = list
.t
.get(1)
.ok_or_else(|| {
Expand All @@ -72,9 +75,14 @@ pub fn expand_templates(mut toplevel_exprs: Vec<TopLevel>) -> Result<Vec<TopLeve
if templates.iter().any(|t| t.name == name) {
bail_expr!(name_expr, "template name was already defined earlier");
}
Ok(name)
})?
.to_owned();
Ok((name, name_expr.span()))
})?;

#[cfg(feature = "lsp")]
lsp_hints
.definition_locations
.template
.insert(name.to_owned(), _name_span);

// Parse template variable names
let vars = list
Expand Down Expand Up @@ -130,7 +138,7 @@ pub fn expand_templates(mut toplevel_exprs: Vec<TopLevel>) -> Result<Vec<TopLeve
}

templates.push(Template {
name,
name: name.to_string(),
vars,
vars_substitute_names,
content,
Expand All @@ -147,7 +155,7 @@ pub fn expand_templates(mut toplevel_exprs: Vec<TopLevel>) -> Result<Vec<TopLeve
})
})
.collect();
expand(&mut toplevels, &templates)?;
expand(&mut toplevels, &templates, lsp_hints)?;

toplevels.into_iter().try_fold(vec![], |mut tls, tl| {
tls.push(match &tl {
Expand All @@ -169,7 +177,7 @@ struct Replacement {
insert_index: usize,
}

fn expand(exprs: &mut Vec<SExpr>, templates: &[Template]) -> Result<()> {
fn expand(exprs: &mut Vec<SExpr>, templates: &[Template], _lsp_hints: &mut LspHints) -> Result<()> {
let mut replacements: Vec<Replacement> = vec![];
for (expr_index, expr) in exprs.iter_mut().enumerate() {
match expr {
Expand All @@ -179,7 +187,7 @@ fn expand(exprs: &mut Vec<SExpr>, templates: &[Template]) -> Result<()> {
l.t.first().and_then(|expr| expr.atom(None)),
Some("template-expand") | Some("t!")
) {
expand(&mut l.t, templates)?;
expand(&mut l.t, templates, _lsp_hints)?;
continue;
}

Expand All @@ -196,6 +204,11 @@ fn expand(exprs: &mut Vec<SExpr>, templates: &[Template]) -> Result<()> {
let name = name_expr.atom(None).ok_or_else(|| {
anyhow_expr!(name_expr, "template name must be a string")
})?;
#[cfg(feature = "lsp")]
_lsp_hints
.reference_locations
.template
.push(name, name_expr.span());
templates.iter().find(|t| t.name == name).ok_or_else(|| {
anyhow_expr!(
name_expr,
Expand Down
21 changes: 21 additions & 0 deletions parser/src/cfg/fake_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,28 @@ use super::*;

use crate::{anyhow_expr, bail, bail_expr};

#[allow(unused_variables)]
fn set_virtual_key_reference_lsp_hint(vk_name_expr: &SExpr, s: &ParserState) {
#[cfg(feature = "lsp")]
{
let atom = match vk_name_expr {
SExpr::Atom(x) => x,
SExpr::List(_) => unreachable!("should be validated to be atom earlier"),
};
s.lsp_hints
.borrow_mut()
.reference_locations
.virtual_key
.push_from_atom(atom);
}
}

pub(crate) fn parse_on_press_fake_key_op(
ac_params: &[SExpr],
s: &ParserState,
) -> Result<&'static KanataAction> {
let (coord, action) = parse_fake_key_op_coord_action(ac_params, s, ON_PRESS_FAKEKEY)?;
set_virtual_key_reference_lsp_hint(&ac_params[0], s);
Ok(s.a.sref(Action::Custom(
s.a.sref(s.a.sref_slice(CustomAction::FakeKey { coord, action })),
)))
Expand All @@ -17,6 +34,7 @@ pub(crate) fn parse_on_release_fake_key_op(
s: &ParserState,
) -> Result<&'static KanataAction> {
let (coord, action) = parse_fake_key_op_coord_action(ac_params, s, ON_RELEASE_FAKEKEY)?;
set_virtual_key_reference_lsp_hint(&ac_params[0], s);
Ok(s.a.sref(Action::Custom(s.a.sref(
s.a.sref_slice(CustomAction::FakeKeyOnRelease { coord, action }),
))))
Expand Down Expand Up @@ -66,6 +84,7 @@ pub(crate) fn parse_on_idle_fakekey(
})?;
let (x, y) = get_fake_key_coords(y);
let coord = Coord { x, y };
set_virtual_key_reference_lsp_hint(&ac_params[0], s);
Ok(s.a.sref(Action::Custom(s.a.sref(s.a.sref_slice(
CustomAction::FakeKeyOnIdle(FakeKeyOnIdle {
coord,
Expand Down Expand Up @@ -115,6 +134,7 @@ fn parse_fake_key_op_coord_action(
)
})?;
let (x, y) = get_fake_key_coords(y);
set_virtual_key_reference_lsp_hint(&ac_params[0], s);
Ok((Coord { x, y }, action))
}

Expand Down Expand Up @@ -168,6 +188,7 @@ fn parse_vkey_coord(param: &SExpr, s: &ParserState) -> Result<Coord> {
None => bail_expr!(param, "unknown virtual key name: {name}",),
};
let coord = Coord { x: FAKE_KEY_ROW, y };
set_virtual_key_reference_lsp_hint(param, s);
Ok(coord)
}

Expand Down
Loading

0 comments on commit 306e172

Please sign in to comment.