Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

preview: show the current method/function when it's invisible #798

Merged
merged 19 commits into from
Feb 9, 2022
Merged
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