Skip to content

Commit

Permalink
preview: show the current method/function when it's invisible (#798)
Browse files Browse the repository at this point in the history
* preview: show the current method/function when it's invisible

Close #663

* Fix test

* Update CHANGELOG.md

* Filter the potential symbols

* Reduce false positives

* Truncate the context line

* .

* .

* .

* .

* .

* .

* .

* Ignore the comment line

* .

* .

* Ensure the results from ctags are sorted in dumb_jump

* par gtags

* Truncate simply
  • Loading branch information
liuchengxu authored Feb 9, 2022
1 parent 40a748c commit 739713b
Show file tree
Hide file tree
Showing 25 changed files with 321 additions and 123 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
## Added

- Support generating the source of `tags` provider using the Rust binary, remove the vista.vim dep from `tags` provider. #795
- Initial support of preview with context. #798

## Fixed

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

111 changes: 95 additions & 16 deletions crates/maple_cli/src/command/ctags/buffer_tags.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::ops::Deref;
use std::path::Path;
use std::sync::atomic::{AtomicUsize, Ordering};

use anyhow::Result;
use anyhow::{Context, Result};
use clap::Parser;
use filter::subprocess::{Exec, Redirection};
use itertools::Itertools;
Expand All @@ -15,6 +16,10 @@ use crate::tools::ctags::CTAGS_HAS_JSON_FEATURE;
/// Prints the tags for a specific file.
#[derive(Parser, Debug, Clone)]
pub struct BufferTags {
/// Show the nearest function/method to a specific line.
#[clap(long)]
current_context: Option<usize>,

/// Use the raw output format even json output is supported, for testing purpose.
#[clap(long)]
force_raw: bool,
Expand All @@ -25,6 +30,13 @@ pub struct BufferTags {

impl BufferTags {
pub fn run(&self, _params: Params) -> Result<()> {
if let Some(at) = self.current_context {
let context_tag = current_context_tag(self.file.as_path(), at)
.context("Error at finding the context tag info")?;
println!("Context: {:?}", context_tag);
return Ok(());
}

let lines = if *CTAGS_HAS_JSON_FEATURE.deref() && !self.force_raw {
let cmd = build_cmd_in_json_format(self.file.as_ref());
buffer_tags_lines_inner(cmd, BufferTagInfo::from_ctags_json)?
Expand All @@ -41,19 +53,68 @@ impl BufferTags {
}
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Default)]
struct BufferTagInfo {
name: String,
pattern: String,
line: usize,
kind: String,
const CONTEXT_KINDS: &[&str] = &[
"function",
"method",
"module",
"macro",
"implementation",
"interface",
];

const CONTEXT_SUPERSET: &[&str] = &[
"function",
"method",
"module",
"macro",
"implementation",
"interface",
"struct",
"field",
"typedef",
"enumerator",
];

/// Returns the method/function context associated with line `at`.
pub fn current_context_tag(file: &Path, at: usize) -> Option<BufferTagInfo> {
let superset_tags = if *CTAGS_HAS_JSON_FEATURE.deref() {
let cmd = build_cmd_in_json_format(file);
collect_superset_context_tags(cmd, BufferTagInfo::from_ctags_json, at).ok()?
} else {
let cmd = build_cmd_in_raw_format(file);
collect_superset_context_tags(cmd, BufferTagInfo::from_ctags_raw, at).ok()?
};

match superset_tags.binary_search_by_key(&at, |tag| tag.line) {
Ok(_l) => None, // Skip if the line is exactly a tag line.
Err(_l) => {
let context_tags = superset_tags
.into_par_iter()
.filter(|tag| CONTEXT_KINDS.contains(&tag.kind.as_ref()))
.collect::<Vec<_>>();

match context_tags.binary_search_by_key(&at, |tag| tag.line) {
Ok(_) => None,
Err(l) => {
let maybe_idx = l.checked_sub(1); // use the previous item.
maybe_idx.and_then(|idx| context_tags.into_iter().nth(idx))
}
}
}
}
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Default)]
pub struct BufferTagInfo {
pub name: String,
pub pattern: String,
pub line: usize,
pub kind: String,
}

impl BufferTagInfo {
/// Returns the display line for BuiltinHandle, no icon attached.
fn format_buffer_tags(&self, max_name_len: usize) -> String {
let pattern_len = self.pattern.len();

let name_line = format!("{}:{}", self.name, self.line);

let kind = format!("[{}]", self.kind);
Expand All @@ -63,7 +124,7 @@ impl BufferTagInfo {
name_group_width = max_name_len + 6,
kind = kind,
kind_width = 10,
pattern = self.pattern[2..pattern_len - 2].trim()
pattern = self.extract_pattern().trim()
)
}

Expand Down Expand Up @@ -105,6 +166,11 @@ impl BufferTagInfo {
None
}
}

pub fn extract_pattern(&self) -> &str {
let pattern_len = self.pattern.len();
&self.pattern[2..pattern_len - 2]
}
}

fn build_cmd_in_json_format(file: impl AsRef<std::ffi::OsStr>) -> Exec {
Expand Down Expand Up @@ -140,14 +206,9 @@ fn buffer_tags_lines_inner(
cmd: Exec,
parse_fn: impl Fn(&str) -> Option<BufferTagInfo> + Send + Sync,
) -> Result<Vec<String>> {
use std::io::BufRead;

let stdout = cmd.stream_stdout()?;

let max_name_len = AtomicUsize::new(0);

let tags = std::io::BufReader::with_capacity(8 * 1024 * 1024, stdout)
.lines()
let tags = crate::utils::lines(cmd)?
.flatten()
.par_bridge()
.filter_map(|s| {
Expand All @@ -166,3 +227,21 @@ fn buffer_tags_lines_inner(
.map(|s| s.format_buffer_tags(max_name_len))
.collect::<Vec<_>>())
}

fn collect_superset_context_tags(
cmd: Exec,
parse_fn: impl Fn(&str) -> Option<BufferTagInfo> + Send + Sync,
target_lnum: usize,
) -> Result<Vec<BufferTagInfo>> {
let mut tags = crate::utils::lines(cmd)?
.flatten()
.par_bridge()
.filter_map(|s| parse_fn(&s))
// the line of method/function name is lower.
.filter(|tag| tag.line <= target_lnum && CONTEXT_SUPERSET.contains(&tag.kind.as_ref()))
.collect::<Vec<_>>();

tags.par_sort_unstable_by_key(|x| x.line);

Ok(tags)
}
2 changes: 1 addition & 1 deletion crates/maple_cli/src/command/grep/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use std::path::{Path, PathBuf};
use std::process::Command;

use anyhow::{Context, Result};
use clap::Parser;
use itertools::Itertools;
use rayon::prelude::*;
use clap::Parser;

use filter::{
matcher::{Bonus, MatchType},
Expand Down
22 changes: 22 additions & 0 deletions crates/maple_cli/src/dumb_analyzer/find_usages/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
mod search_engine;

use std::collections::HashMap;
use std::ops::{Index, IndexMut};

use once_cell::sync::OnceCell;
use rayon::prelude::*;

pub use self::search_engine::{CtagsSearcher, GtagsSearcher, RegexSearcher, SearchType};

/// Returns a list of comment prefix for a source file.
///
/// # Argument
///
/// - `ext`: the extension of a file, e.g., `rs`.
pub fn get_comments_by_ext(ext: &str) -> &[&str] {
static LANGUAGE_COMMENT_TABLE: OnceCell<HashMap<&str, Vec<&str>>> = OnceCell::new();

let table = LANGUAGE_COMMENT_TABLE.get_or_init(|| {
serde_json::from_str(include_str!(
"../../../../../scripts/dumb_jump/comments_map.json"
))
.expect("Wrong path for comments_map.json")
});

table
.get(ext)
.unwrap_or_else(|| table.get("*").expect("`*` entry exists; qed"))
}

#[derive(Clone, Debug, Default)]
pub struct Usage {
pub line: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,13 @@ impl<'a, P: AsRef<Path> + Hash> CtagsSearcher<'a, P> {
search_type: SearchType,
force_generate: bool,
) -> Result<impl Iterator<Item = TagInfo>> {
use std::io::BufRead;

if force_generate || !self.tags_exists() {
self.generate_tags()?;
}

let stdout = self.build_exec(query, search_type).stream_stdout()?;
let cmd = self.build_exec(query, search_type);

// We usually have a decent amount of RAM nowdays.
Ok(std::io::BufReader::with_capacity(8 * 1024 * 1024, stdout)
.lines()
Ok(crate::utils::lines(cmd)?
.flatten()
.filter_map(|s| TagInfo::from_readtags(&s)))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::io::BufRead;
use std::path::{PathBuf, MAIN_SEPARATOR};

use anyhow::{anyhow, Result};
Expand Down Expand Up @@ -121,11 +120,7 @@ impl GtagsSearcher {

// Returns a stream of tag parsed from the gtags output.
fn execute(cmd: Exec) -> Result<impl Iterator<Item = TagInfo>> {
let stdout = cmd.stream_stdout()?;

// We usually have a decent amount of RAM nowdays.
Ok(std::io::BufReader::with_capacity(8 * 1024 * 1024, stdout)
.lines()
Ok(crate::utils::lines(cmd)?
.flatten()
.filter_map(|s| TagInfo::from_gtags(&s)))
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{collections::HashMap, fmt::Display};

use anyhow::{anyhow, Result};
use itertools::Itertools;
use once_cell::sync::{Lazy, OnceCell};
use once_cell::sync::Lazy;
use rayon::prelude::*;
use serde::Deserialize;

Expand Down Expand Up @@ -106,25 +106,6 @@ pub fn get_language_by_ext(ext: &str) -> Result<&&str> {
.ok_or_else(|| anyhow!("dumb_analyzer is unsupported for {}", ext))
}

/// Map of file extension to the comment prefix.
///
/// Keyed by the extension name.
pub fn get_comments_by_ext(ext: &str) -> &[&str] {
static LANGUAGE_COMMENT_TABLE: OnceCell<HashMap<&str, Vec<&str>>> = OnceCell::new();

let table = LANGUAGE_COMMENT_TABLE.get_or_init(|| {
let comments: HashMap<&str, Vec<&str>> = serde_json::from_str(include_str!(
"../../../../../../../scripts/dumb_jump/comments_map.json"
))
.expect("Wrong path for comments_map.json");
comments
});

table
.get(ext)
.unwrap_or_else(|| table.get("*").expect("`*` entry exists; qed"))
}

/// Type of match result of ripgrep.
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Hash)]
pub enum MatchKind {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ use anyhow::Result;
use rayon::prelude::*;

use self::definition::{
definitions_and_references, do_search_usages, get_comments_by_ext, get_language_by_ext,
MatchKind,
definitions_and_references, do_search_usages, get_language_by_ext, MatchKind,
};
use self::worker::find_occurrences_by_ext;
use crate::dumb_analyzer::find_usages::{Usage, Usages};
use crate::dumb_analyzer::{
find_usages::{Usage, Usages},
get_comments_by_ext,
};
use crate::tools::ripgrep::{Match, Word};
use crate::utils::ExactOrInverseTerms;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use std::path::PathBuf;
use anyhow::Result;
use rayon::prelude::*;

use super::definition::{build_full_regexp, get_comments_by_ext, is_comment, DefinitionKind};
use super::definition::{build_full_regexp, is_comment, DefinitionKind};
use crate::dumb_analyzer::get_comments_by_ext;
use crate::process::AsyncCommand;
use crate::tools::ripgrep::{Match, Word};

Expand Down
2 changes: 1 addition & 1 deletion crates/maple_cli/src/dumb_analyzer/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
mod find_usages;

pub use self::find_usages::{
CtagsSearcher, GtagsSearcher, RegexSearcher, SearchType, Usage, Usages,
get_comments_by_ext, CtagsSearcher, GtagsSearcher, RegexSearcher, SearchType, Usage, Usages,
};
12 changes: 9 additions & 3 deletions crates/maple_cli/src/previewer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::path::Path;

use anyhow::{anyhow, Result};

use types::PreviewInfo;
use utility::{read_first_lines, read_preview_lines};

#[inline]
Expand Down Expand Up @@ -61,10 +62,15 @@ pub fn preview_file_at<P: AsRef<Path>>(
) -> Result<(Vec<String>, usize)> {
tracing::debug!(path = %path.as_ref().display(), lnum, "Previewing file");

let (lines_iter, hi_lnum) = read_preview_lines(path.as_ref(), lnum, half_size)?;
let PreviewInfo {
lines,
highlight_lnum,
..
} = read_preview_lines(path.as_ref(), lnum, half_size)?;

let lines = std::iter::once(format!("{}:{}", path.as_ref().display(), lnum))
.chain(truncate_preview_lines(max_width, lines_iter.into_iter()))
.chain(truncate_preview_lines(max_width, lines.into_iter()))
.collect::<Vec<_>>();

Ok((lines, hi_lnum))
Ok((lines, highlight_lnum))
}
4 changes: 2 additions & 2 deletions crates/maple_cli/src/stdio_server/providers/builtin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl EventHandle for BuiltinHandle {
async fn on_move(&mut self, msg: MethodCall, context: Arc<SessionContext>) -> Result<()> {
let msg_id = msg.id;

let source_scale = context.source_scale.lock();
let source_scale = context.state.source_scale.lock();

let curline = match (source_scale.deref(), msg.get_u64("lnum").ok()) {
(SourceScale::Small { ref lines, .. }, Some(lnum)) => {
Expand Down Expand Up @@ -67,7 +67,7 @@ impl EventHandle for BuiltinHandle {
async fn on_typed(&mut self, msg: MethodCall, context: Arc<SessionContext>) -> Result<()> {
let query = msg.get_query();

let source_scale = context.source_scale.lock();
let source_scale = context.state.source_scale.lock();

match source_scale.deref() {
SourceScale::Small { ref lines, .. } => {
Expand Down
Loading

0 comments on commit 739713b

Please sign in to comment.