Skip to content

Commit

Permalink
Improve ctags plugin (#1106)
Browse files Browse the repository at this point in the history
* Print a warn log if ctags executable is not found

* .

* Refactor CTAGS_BIN

* .

* ci: remove clippy override

* Update CI

* Nits

* Update to 1.83

* Fix clippy

* Fixes
  • Loading branch information
liuchengxu authored Dec 12, 2024
1 parent 66b6154 commit 515e464
Show file tree
Hide file tree
Showing 13 changed files with 102 additions and 56 deletions.
26 changes: 9 additions & 17 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,23 @@ jobs:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: nightly
components: rustfmt
override: true
- uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- name: Run fmt
run: cargo +nightly fmt --all -- --check

clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: clippy
override: true
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features --all-targets --manifest-path Cargo.toml -- -D warnings
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- name: Run clippy
run: |
cargo clippy --locked --all-features --all-targets --manifest-path Cargo.toml -- -D warnings
crates:
name: Rust Tests
Expand Down
9 changes: 2 additions & 7 deletions crates/cli/src/command/ctags/recursive_tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ use clap::Parser;
use filter::{FilterContext, SequentialSource};
use itertools::Itertools;
use maple_core::process::ShellCommand;
use maple_core::tools::ctags::{ProjectCtagsCommand, CTAGS_HAS_JSON_FEATURE};
use maple_core::tools::ctags::{ProjectCtagsCommand, CTAGS_BIN};
use matcher::{MatchScope, MatcherBuilder};
use rayon::prelude::*;
use std::ops::Deref;
use std::sync::Arc;
use types::ClapItem;

Expand Down Expand Up @@ -65,11 +64,7 @@ impl RecursiveTags {
..
}: Args,
) -> Result<()> {
if !CTAGS_HAS_JSON_FEATURE.deref() {
return Err(anyhow::anyhow!(
"ctags executable is not compiled with +json feature, please recompile it."
));
}
CTAGS_BIN.ensure_json_feature()?;

let mut ctags_cmd = self.project_ctags_cmd()?;

Expand Down
2 changes: 1 addition & 1 deletion crates/code_tools/src/linting/linters/typos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ enum Message<'m> {
Typo(Typo<'m>),
}

impl<'m> Message<'m> {
impl Message<'_> {
fn try_into_diagnostic(self) -> Option<Diagnostic> {
match self {
Self::Typo(typo) => {
Expand Down
2 changes: 1 addition & 1 deletion crates/code_tools/src/linting/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ enum WorkspaceMarker {
}

impl WorkspaceMarker {
fn find_workspace<'a>(&'a self, source_file: &'a Path) -> Option<&Path> {
fn find_workspace<'a>(&self, source_file: &'a Path) -> Option<&'a Path> {
match self {
Self::RootMarkers(root_markers) => paths::find_project_root(source_file, root_markers),
Self::ParentOfSourceFile => Some(source_file.parent().unwrap_or(source_file)),
Expand Down
14 changes: 12 additions & 2 deletions crates/maple_core/src/stdio_server/diagnostics_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,16 @@ fn update_buffer_diagnostics(
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
.is_ok();

tracing::info!(
?bufnr,
?is_first_result,
"[update_buffer_diagnostics] buffer_diagnostics: {buffer_diagnostics:?}"
);
tracing::info!(
?bufnr,
"[update_buffer_diagnostics] new_diagnostics: {new_diagnostics:?}"
);

let new_stats = if is_first_result {
let _ = vim.exec(
"clap#plugin#diagnostics#refresh_highlights",
Expand All @@ -206,12 +216,12 @@ fn update_buffer_diagnostics(
.filter(|d| !existing.contains(d))
.collect::<Vec<_>>();

followup_diagnostics.dedup();

// Must drop the lock otherwise the deadlock occurs as
// the write lock will be acquired later.
drop(existing);

followup_diagnostics.dedup();

if !followup_diagnostics.is_empty() {
let _ = vim.exec(
"clap#plugin#diagnostics#add_highlights",
Expand Down
6 changes: 5 additions & 1 deletion crates/maple_core/src/stdio_server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,11 @@ fn initialize_service(vim: Vim) -> InitializedService {
}

if plugin_config.ctags.enable {
register_plugin(Box::new(CtagsPlugin::new(vim.clone())), None);
if crate::tools::ctags::CTAGS_BIN.is_available() {
register_plugin(Box::new(CtagsPlugin::new(vim.clone())), None);
} else {
tracing::warn!("Failed to register ctags plugin as ctags executable not found");
}
}

if plugin_config.markdown.enable {
Expand Down
9 changes: 7 additions & 2 deletions crates/maple_core/src/stdio_server/plugin/system.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,21 @@ fn parse_vim_which_key_map(config_file: &str) -> HashMap<char, HashMap<char, Str
}

fn note_recent_file(file_path: String) {
tracing::debug!(?file_path, "Received a recent file notification");

let Ok(path) = std::fs::canonicalize(&file_path) else {
tracing::debug!(?file_path, "Ignored a recent file notification");
return;
};

if !path.exists() || !path.is_file() {
tracing::debug!(
?file_path,
"Ignored a recent file notification: not a valid file"
);
return;
}

tracing::debug!("Received a recent file notification: {}", path.display());

let mut recent_files = RECENT_FILES_IN_MEMORY.write();
recent_files.upsert(file_path);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::stdio_server::provider::{
BaseArgs, ClapProvider, Context, ProviderError, ProviderResult,
};
use crate::stdio_server::vim::VimResult;
use crate::tools::ctags::{get_language, TagsGenerator, CTAGS_EXISTS};
use crate::tools::ctags::{get_language, TagsGenerator, CTAGS_BIN};
use crate::tools::gtags::GTAGS_EXISTS;
use filter::Query;
use futures::Future;
Expand Down Expand Up @@ -214,7 +214,7 @@ impl DumbJumpProvider {
});
}

match (*CTAGS_EXISTS, *GTAGS_EXISTS) {
match (CTAGS_BIN.is_available(), *GTAGS_EXISTS) {
(true, true) => run(
async move {
futures::future::join(ctags_future, gtags_future).await;
Expand Down
13 changes: 6 additions & 7 deletions crates/maple_core/src/tools/ctags/context_tag.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use crate::tools::ctags::{BufferTag, CTAGS_HAS_JSON_FEATURE};
use crate::tools::ctags::{BufferTag, CTAGS_BIN};
use rayon::prelude::*;
use std::io::Result;
use std::ops::Deref;
use std::path::Path;
use std::process::Stdio;
use std::sync::atomic::{AtomicUsize, Ordering};
Expand Down Expand Up @@ -94,7 +93,7 @@ fn find_context_tag(superset_tags: Vec<BufferTag>, at: usize) -> Option<BufferTa
///
/// NOTE: I don't know why, but this may take forever to complete somehow, making the async runtime blocked.
pub async fn current_context_tag_async(file: &Path, at: usize) -> Option<BufferTag> {
let superset_tags = if *CTAGS_HAS_JSON_FEATURE.deref() {
let superset_tags = if CTAGS_BIN.has_json_feature() {
collect_superset_context_tags_async(tokio_cmd(file, true), BufferTag::from_json_line, at)
.await
} else {
Expand All @@ -107,7 +106,7 @@ pub async fn current_context_tag_async(file: &Path, at: usize) -> Option<BufferT

/// Returns the method/function context associated with line `at`.
pub fn current_context_tag(file: &Path, at: usize) -> Option<BufferTag> {
let superset_tags = if *CTAGS_HAS_JSON_FEATURE.deref() {
let superset_tags = if CTAGS_BIN.has_json_feature() {
collect_superset_context_tags(subprocess_cmd(file, true), BufferTag::from_json_line, at)
} else {
collect_superset_context_tags(subprocess_cmd(file, false), BufferTag::from_raw_line, at)
Expand All @@ -120,7 +119,7 @@ pub fn buffer_tags_lines(
file: impl AsRef<std::ffi::OsStr>,
force_raw: bool,
) -> Result<Vec<String>> {
let (tags, max_name_len) = if *CTAGS_HAS_JSON_FEATURE.deref() && !force_raw {
let (tags, max_name_len) = if CTAGS_BIN.has_json_feature() && !force_raw {
collect_buffer_tags(subprocess_cmd(file, true), BufferTag::from_json_line)?
} else {
collect_buffer_tags(subprocess_cmd(file, false), BufferTag::from_raw_line)?
Expand All @@ -133,7 +132,7 @@ pub fn buffer_tags_lines(
}

pub fn fetch_buffer_tags(file: impl AsRef<std::ffi::OsStr>) -> Result<Vec<BufferTag>> {
let (mut tags, _max_name_len) = if *CTAGS_HAS_JSON_FEATURE.deref() {
let (mut tags, _max_name_len) = if CTAGS_BIN.has_json_feature() {
collect_buffer_tags(subprocess_cmd(file, true), BufferTag::from_json_line)?
} else {
collect_buffer_tags(subprocess_cmd(file, false), BufferTag::from_raw_line)?
Expand All @@ -148,7 +147,7 @@ pub fn buffer_tag_items(
file: impl AsRef<std::ffi::OsStr>,
force_raw: bool,
) -> Result<Vec<Arc<dyn ClapItem>>> {
let (tags, max_name_len) = if *CTAGS_HAS_JSON_FEATURE.deref() && !force_raw {
let (tags, max_name_len) = if CTAGS_BIN.has_json_feature() && !force_raw {
collect_buffer_tags(subprocess_cmd(file, true), BufferTag::from_json_line)?
} else {
collect_buffer_tags(subprocess_cmd(file, false), BufferTag::from_raw_line)?
Expand Down
65 changes: 53 additions & 12 deletions crates/maple_core/src/tools/ctags/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,50 @@ pub static CTAGS_TAGS_DIR: Lazy<PathBuf> = Lazy::new(|| {
tags_dir
});

pub static CTAGS_EXISTS: Lazy<bool> = Lazy::new(|| {
std::process::Command::new("ctags")
pub enum CtagsBinary {
/// ctags executable exists.
Available {
/// Whether the ctags executable supports `--output-format=json`.
json_feature: bool,
},
/// ctags executable does not exist.
NotFound,
}

impl CtagsBinary {
pub fn is_available(&self) -> bool {
matches!(self, Self::Available { .. })
}

pub fn has_json_feature(&self) -> bool {
match self {
Self::Available { json_feature } => *json_feature,
Self::NotFound => false,
}
}

pub fn ensure_json_feature(&self) -> std::io::Result<()> {
match self {
Self::Available { json_feature } => {
if *json_feature {
Ok(())
} else {
Err(Error::new(
ErrorKind::Other,
"ctags executable is not compiled with +json feature, please recompile it.",
))
}
}
Self::NotFound => Err(Error::new(
ErrorKind::NotFound,
"ctags executable not found",
)),
}
}
}

pub static CTAGS_BIN: Lazy<CtagsBinary> = Lazy::new(|| {
let ctags_exist = std::process::Command::new("ctags")
.arg("--version")
.stderr(std::process::Stdio::inherit())
.output()
Expand All @@ -53,25 +95,24 @@ pub static CTAGS_EXISTS: Lazy<bool> = Lazy::new(|| {
.next()
.map(|line| line.starts_with("Universal Ctags"))
})
.unwrap_or(false)
});
.unwrap_or(false);

/// If the ctags executable supports `--output-format=json`.
pub static CTAGS_HAS_JSON_FEATURE: Lazy<bool> = Lazy::new(|| {
fn detect_json_feature() -> std::io::Result<bool> {
let output = std::process::Command::new("ctags")
.arg("--list-features")
.stderr(std::process::Stdio::inherit())
.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
if stdout.split('\n').any(|x| x.starts_with("json")) {
Ok(true)
} else {
Err(Error::new(ErrorKind::Other, "ctags has no +json feature"))
}
Ok(stdout.split('\n').any(|x| x.starts_with("json")))
}

detect_json_feature().unwrap_or(false)
if ctags_exist {
CtagsBinary::Available {
json_feature: detect_json_feature().unwrap_or(false),
}
} else {
CtagsBinary::NotFound
}
});

/// Used to specify the language when working with `readtags`.
Expand Down
2 changes: 1 addition & 1 deletion crates/rpc/src/jsonrpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl Serialize for Version {

struct VersionVisitor;

impl<'v> serde::de::Visitor<'v> for VersionVisitor {
impl serde::de::Visitor<'_> for VersionVisitor {
type Value = Version;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
Expand Down
4 changes: 2 additions & 2 deletions crates/tree_sitter/src/utf8_char_indices.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ impl<'a> UncheckedUtf8CharIndices<'a> {
}
}

impl<'a> Iterator for UncheckedUtf8CharIndices<'a> {
impl Iterator for UncheckedUtf8CharIndices<'_> {
type Item = (usize, char);

fn next(&mut self) -> Option<Self::Item> {
Expand Down Expand Up @@ -49,7 +49,7 @@ impl<'a> Utf8CharIndices<'a> {
}
}

impl<'a> Iterator for Utf8CharIndices<'a> {
impl Iterator for Utf8CharIndices<'_> {
type Item = (usize, char);

fn next(&mut self) -> Option<Self::Item> {
Expand Down
2 changes: 1 addition & 1 deletion rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[toolchain]
channel = "1.80"
channel = "1.83"

0 comments on commit 515e464

Please sign in to comment.