Skip to content

Commit 515e464

Browse files
authored
Improve ctags plugin (#1106)
* 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
1 parent 66b6154 commit 515e464

File tree

13 files changed

+102
-56
lines changed

13 files changed

+102
-56
lines changed

.github/workflows/ci.yml

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,31 +24,23 @@ jobs:
2424
name: Rustfmt
2525
runs-on: ubuntu-latest
2626
steps:
27-
- uses: actions/checkout@v2
28-
- uses: actions-rs/toolchain@v1
27+
- uses: actions/checkout@v4
28+
- uses: dtolnay/rust-toolchain@stable
2929
with:
3030
toolchain: nightly
3131
components: rustfmt
32-
override: true
33-
- uses: actions-rs/cargo@v1
34-
with:
35-
command: fmt
36-
args: --all -- --check
32+
- name: Run fmt
33+
run: cargo +nightly fmt --all -- --check
3734

3835
clippy:
3936
name: Clippy
4037
runs-on: ubuntu-latest
4138
steps:
42-
- uses: actions/checkout@v2
43-
- uses: actions-rs/toolchain@v1
44-
with:
45-
toolchain: stable
46-
components: clippy
47-
override: true
48-
- uses: actions-rs/clippy-check@v1
49-
with:
50-
token: ${{ secrets.GITHUB_TOKEN }}
51-
args: --all-features --all-targets --manifest-path Cargo.toml -- -D warnings
39+
- uses: actions/checkout@v4
40+
- uses: dtolnay/rust-toolchain@stable
41+
- name: Run clippy
42+
run: |
43+
cargo clippy --locked --all-features --all-targets --manifest-path Cargo.toml -- -D warnings
5244
5345
crates:
5446
name: Rust Tests

crates/cli/src/command/ctags/recursive_tags.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@ use clap::Parser;
66
use filter::{FilterContext, SequentialSource};
77
use itertools::Itertools;
88
use maple_core::process::ShellCommand;
9-
use maple_core::tools::ctags::{ProjectCtagsCommand, CTAGS_HAS_JSON_FEATURE};
9+
use maple_core::tools::ctags::{ProjectCtagsCommand, CTAGS_BIN};
1010
use matcher::{MatchScope, MatcherBuilder};
1111
use rayon::prelude::*;
12-
use std::ops::Deref;
1312
use std::sync::Arc;
1413
use types::ClapItem;
1514

@@ -65,11 +64,7 @@ impl RecursiveTags {
6564
..
6665
}: Args,
6766
) -> Result<()> {
68-
if !CTAGS_HAS_JSON_FEATURE.deref() {
69-
return Err(anyhow::anyhow!(
70-
"ctags executable is not compiled with +json feature, please recompile it."
71-
));
72-
}
67+
CTAGS_BIN.ensure_json_feature()?;
7368

7469
let mut ctags_cmd = self.project_ctags_cmd()?;
7570

crates/code_tools/src/linting/linters/typos.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ enum Message<'m> {
7676
Typo(Typo<'m>),
7777
}
7878

79-
impl<'m> Message<'m> {
79+
impl Message<'_> {
8080
fn try_into_diagnostic(self) -> Option<Diagnostic> {
8181
match self {
8282
Self::Typo(typo) => {

crates/code_tools/src/linting/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ enum WorkspaceMarker {
119119
}
120120

121121
impl WorkspaceMarker {
122-
fn find_workspace<'a>(&'a self, source_file: &'a Path) -> Option<&Path> {
122+
fn find_workspace<'a>(&self, source_file: &'a Path) -> Option<&'a Path> {
123123
match self {
124124
Self::RootMarkers(root_markers) => paths::find_project_root(source_file, root_markers),
125125
Self::ParentOfSourceFile => Some(source_file.parent().unwrap_or(source_file)),

crates/maple_core/src/stdio_server/diagnostics_worker.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,16 @@ fn update_buffer_diagnostics(
191191
.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
192192
.is_ok();
193193

194+
tracing::info!(
195+
?bufnr,
196+
?is_first_result,
197+
"[update_buffer_diagnostics] buffer_diagnostics: {buffer_diagnostics:?}"
198+
);
199+
tracing::info!(
200+
?bufnr,
201+
"[update_buffer_diagnostics] new_diagnostics: {new_diagnostics:?}"
202+
);
203+
194204
let new_stats = if is_first_result {
195205
let _ = vim.exec(
196206
"clap#plugin#diagnostics#refresh_highlights",
@@ -206,12 +216,12 @@ fn update_buffer_diagnostics(
206216
.filter(|d| !existing.contains(d))
207217
.collect::<Vec<_>>();
208218

209-
followup_diagnostics.dedup();
210-
211219
// Must drop the lock otherwise the deadlock occurs as
212220
// the write lock will be acquired later.
213221
drop(existing);
214222

223+
followup_diagnostics.dedup();
224+
215225
if !followup_diagnostics.is_empty() {
216226
let _ = vim.exec(
217227
"clap#plugin#diagnostics#add_highlights",

crates/maple_core/src/stdio_server/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,11 @@ fn initialize_service(vim: Vim) -> InitializedService {
165165
}
166166

167167
if plugin_config.ctags.enable {
168-
register_plugin(Box::new(CtagsPlugin::new(vim.clone())), None);
168+
if crate::tools::ctags::CTAGS_BIN.is_available() {
169+
register_plugin(Box::new(CtagsPlugin::new(vim.clone())), None);
170+
} else {
171+
tracing::warn!("Failed to register ctags plugin as ctags executable not found");
172+
}
169173
}
170174

171175
if plugin_config.markdown.enable {

crates/maple_core/src/stdio_server/plugin/system.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,21 @@ fn parse_vim_which_key_map(config_file: &str) -> HashMap<char, HashMap<char, Str
7878
}
7979

8080
fn note_recent_file(file_path: String) {
81-
tracing::debug!(?file_path, "Received a recent file notification");
82-
8381
let Ok(path) = std::fs::canonicalize(&file_path) else {
82+
tracing::debug!(?file_path, "Ignored a recent file notification");
8483
return;
8584
};
8685

8786
if !path.exists() || !path.is_file() {
87+
tracing::debug!(
88+
?file_path,
89+
"Ignored a recent file notification: not a valid file"
90+
);
8891
return;
8992
}
9093

94+
tracing::debug!("Received a recent file notification: {}", path.display());
95+
9196
let mut recent_files = RECENT_FILES_IN_MEMORY.write();
9297
recent_files.upsert(file_path);
9398
}

crates/maple_core/src/stdio_server/provider/impls/dumb_jump/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::stdio_server::provider::{
88
BaseArgs, ClapProvider, Context, ProviderError, ProviderResult,
99
};
1010
use crate::stdio_server::vim::VimResult;
11-
use crate::tools::ctags::{get_language, TagsGenerator, CTAGS_EXISTS};
11+
use crate::tools::ctags::{get_language, TagsGenerator, CTAGS_BIN};
1212
use crate::tools::gtags::GTAGS_EXISTS;
1313
use filter::Query;
1414
use futures::Future;
@@ -214,7 +214,7 @@ impl DumbJumpProvider {
214214
});
215215
}
216216

217-
match (*CTAGS_EXISTS, *GTAGS_EXISTS) {
217+
match (CTAGS_BIN.is_available(), *GTAGS_EXISTS) {
218218
(true, true) => run(
219219
async move {
220220
futures::future::join(ctags_future, gtags_future).await;

crates/maple_core/src/tools/ctags/context_tag.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
use crate::tools::ctags::{BufferTag, CTAGS_HAS_JSON_FEATURE};
1+
use crate::tools::ctags::{BufferTag, CTAGS_BIN};
22
use rayon::prelude::*;
33
use std::io::Result;
4-
use std::ops::Deref;
54
use std::path::Path;
65
use std::process::Stdio;
76
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -94,7 +93,7 @@ fn find_context_tag(superset_tags: Vec<BufferTag>, at: usize) -> Option<BufferTa
9493
///
9594
/// NOTE: I don't know why, but this may take forever to complete somehow, making the async runtime blocked.
9695
pub async fn current_context_tag_async(file: &Path, at: usize) -> Option<BufferTag> {
97-
let superset_tags = if *CTAGS_HAS_JSON_FEATURE.deref() {
96+
let superset_tags = if CTAGS_BIN.has_json_feature() {
9897
collect_superset_context_tags_async(tokio_cmd(file, true), BufferTag::from_json_line, at)
9998
.await
10099
} else {
@@ -107,7 +106,7 @@ pub async fn current_context_tag_async(file: &Path, at: usize) -> Option<BufferT
107106

108107
/// Returns the method/function context associated with line `at`.
109108
pub fn current_context_tag(file: &Path, at: usize) -> Option<BufferTag> {
110-
let superset_tags = if *CTAGS_HAS_JSON_FEATURE.deref() {
109+
let superset_tags = if CTAGS_BIN.has_json_feature() {
111110
collect_superset_context_tags(subprocess_cmd(file, true), BufferTag::from_json_line, at)
112111
} else {
113112
collect_superset_context_tags(subprocess_cmd(file, false), BufferTag::from_raw_line, at)
@@ -120,7 +119,7 @@ pub fn buffer_tags_lines(
120119
file: impl AsRef<std::ffi::OsStr>,
121120
force_raw: bool,
122121
) -> Result<Vec<String>> {
123-
let (tags, max_name_len) = if *CTAGS_HAS_JSON_FEATURE.deref() && !force_raw {
122+
let (tags, max_name_len) = if CTAGS_BIN.has_json_feature() && !force_raw {
124123
collect_buffer_tags(subprocess_cmd(file, true), BufferTag::from_json_line)?
125124
} else {
126125
collect_buffer_tags(subprocess_cmd(file, false), BufferTag::from_raw_line)?
@@ -133,7 +132,7 @@ pub fn buffer_tags_lines(
133132
}
134133

135134
pub fn fetch_buffer_tags(file: impl AsRef<std::ffi::OsStr>) -> Result<Vec<BufferTag>> {
136-
let (mut tags, _max_name_len) = if *CTAGS_HAS_JSON_FEATURE.deref() {
135+
let (mut tags, _max_name_len) = if CTAGS_BIN.has_json_feature() {
137136
collect_buffer_tags(subprocess_cmd(file, true), BufferTag::from_json_line)?
138137
} else {
139138
collect_buffer_tags(subprocess_cmd(file, false), BufferTag::from_raw_line)?
@@ -148,7 +147,7 @@ pub fn buffer_tag_items(
148147
file: impl AsRef<std::ffi::OsStr>,
149148
force_raw: bool,
150149
) -> Result<Vec<Arc<dyn ClapItem>>> {
151-
let (tags, max_name_len) = if *CTAGS_HAS_JSON_FEATURE.deref() && !force_raw {
150+
let (tags, max_name_len) = if CTAGS_BIN.has_json_feature() && !force_raw {
152151
collect_buffer_tags(subprocess_cmd(file, true), BufferTag::from_json_line)?
153152
} else {
154153
collect_buffer_tags(subprocess_cmd(file, false), BufferTag::from_raw_line)?

crates/maple_core/src/tools/ctags/mod.rs

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,50 @@ pub static CTAGS_TAGS_DIR: Lazy<PathBuf> = Lazy::new(|| {
4040
tags_dir
4141
});
4242

43-
pub static CTAGS_EXISTS: Lazy<bool> = Lazy::new(|| {
44-
std::process::Command::new("ctags")
43+
pub enum CtagsBinary {
44+
/// ctags executable exists.
45+
Available {
46+
/// Whether the ctags executable supports `--output-format=json`.
47+
json_feature: bool,
48+
},
49+
/// ctags executable does not exist.
50+
NotFound,
51+
}
52+
53+
impl CtagsBinary {
54+
pub fn is_available(&self) -> bool {
55+
matches!(self, Self::Available { .. })
56+
}
57+
58+
pub fn has_json_feature(&self) -> bool {
59+
match self {
60+
Self::Available { json_feature } => *json_feature,
61+
Self::NotFound => false,
62+
}
63+
}
64+
65+
pub fn ensure_json_feature(&self) -> std::io::Result<()> {
66+
match self {
67+
Self::Available { json_feature } => {
68+
if *json_feature {
69+
Ok(())
70+
} else {
71+
Err(Error::new(
72+
ErrorKind::Other,
73+
"ctags executable is not compiled with +json feature, please recompile it.",
74+
))
75+
}
76+
}
77+
Self::NotFound => Err(Error::new(
78+
ErrorKind::NotFound,
79+
"ctags executable not found",
80+
)),
81+
}
82+
}
83+
}
84+
85+
pub static CTAGS_BIN: Lazy<CtagsBinary> = Lazy::new(|| {
86+
let ctags_exist = std::process::Command::new("ctags")
4587
.arg("--version")
4688
.stderr(std::process::Stdio::inherit())
4789
.output()
@@ -53,25 +95,24 @@ pub static CTAGS_EXISTS: Lazy<bool> = Lazy::new(|| {
5395
.next()
5496
.map(|line| line.starts_with("Universal Ctags"))
5597
})
56-
.unwrap_or(false)
57-
});
98+
.unwrap_or(false);
5899

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

74-
detect_json_feature().unwrap_or(false)
109+
if ctags_exist {
110+
CtagsBinary::Available {
111+
json_feature: detect_json_feature().unwrap_or(false),
112+
}
113+
} else {
114+
CtagsBinary::NotFound
115+
}
75116
});
76117

77118
/// Used to specify the language when working with `readtags`.

0 commit comments

Comments
 (0)