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

bug(indent): tabs should be displayed with the listchars for tabs #334

Closed
4 tasks done
hankertrix opened this issue Dec 17, 2024 · 3 comments · May be fixed by #305
Closed
4 tasks done

bug(indent): tabs should be displayed with the listchars for tabs #334

hankertrix opened this issue Dec 17, 2024 · 3 comments · May be fixed by #305
Labels
bug Something isn't working

Comments

@hankertrix
Copy link
Contributor

Did you check docs and existing issues?

  • I have read all the snacks.nvim docs
  • I have updated the plugin to the latest version before submitting this issue
  • I have searched the existing issues of snacks.nvim
  • I have searched the existing issues of plugins related to this issue

Neovim version (nvim -v)

NVIM v0.10.2 Build type: RelWithDebInfo LuaJIT 2.1.1731601260 Run "nvim -V1 -v" for more info

Operating system/version

Arch Linux

Describe the bug

Tabs in files are displayed using the space or leading space character from the listchars. They should instead be displayed using the tab character instead.

image

Steps To Reproduce

Here is an example listchars configuration to reproduce the issue:

vim.opt.list = true
vim.opt.listchars = {
    lead = "·",
    trail = "•",
    multispace = "∅",
    nbsp = "‡",
    tab = "⇥»",
    precedes = "❮",
    extends = "❯",
    eol = "↵",
    -- eol = "⤶",
}

Below is an example file to reproduce the issue:

use globset::GlobBuilder;
use mlua::{ExternalError, ExternalResult, Function, IntoLua, IntoLuaMulti, Lua, MetaMethod, Table, Value};
use tokio::fs;
use yazi_fs::remove_dir_clean;

use crate::{Error, bindings::{Cast, Cha}, file::File, url::{Url, UrlRef}};

pub fn compose(lua: &Lua) -> mlua::Result<Table> {
	let index = lua.create_function(|lua, (ts, key): (Table, mlua::String)| {
		let value = match key.as_bytes().as_ref() {
			b"cha" => cha(lua)?,
			b"write" => write(lua)?,
			b"remove" => remove(lua)?,
			b"read_dir" => read_dir(lua)?,
			b"unique_name" => unique_name(lua)?,
			_ => return Ok(Value::Nil),
		}
		.into_lua(lua)?;

		ts.raw_set(key, value.clone())?;
		Ok(value)
	})?;

	let fs = lua.create_table_with_capacity(0, 10)?;
	fs.set_metatable(Some(lua.create_table_from([(MetaMethod::Index.name(), index)])?));

	Ok(fs)
}

fn cha(lua: &Lua) -> mlua::Result<Function> {
	lua.create_async_function(|lua, (url, follow): (UrlRef, Option<bool>)| async move {
		let meta = if follow.unwrap_or(false) {
			fs::metadata(&*url).await
		} else {
			fs::symlink_metadata(&*url).await
		};

		match meta {
			Ok(m) => (Cha::from(m), Value::Nil).into_lua_multi(&lua),
			Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
		}
	})
}

fn write(lua: &Lua) -> mlua::Result<Function> {
	lua.create_async_function(|lua, (url, data): (UrlRef, mlua::String)| async move {
		match fs::write(&*url, data.as_bytes()).await {
			Ok(_) => (true, Value::Nil).into_lua_multi(&lua),
			Err(e) => (false, Error::Io(e)).into_lua_multi(&lua),
		}
	})
}

fn remove(lua: &Lua) -> mlua::Result<Function> {
	lua.create_async_function(|lua, (type_, url): (mlua::String, UrlRef)| async move {
		let result = match type_.as_bytes().as_ref() {
			b"file" => fs::remove_file(&*url).await,
			b"dir" => fs::remove_dir(&*url).await,
			b"dir_all" => fs::remove_dir_all(&*url).await,
			b"dir_clean" => Ok(remove_dir_clean(&url).await),
			_ => Err("Removal type must be 'file', 'dir', 'dir_all', or 'dir_clean'".into_lua_err())?,
		};

		match result {
			Ok(_) => (true, Value::Nil).into_lua_multi(&lua),
			Err(e) => (false, Error::Io(e)).into_lua_multi(&lua),
		}
	})
}

fn read_dir(lua: &Lua) -> mlua::Result<Function> {
	lua.create_async_function(|lua, (dir, options): (UrlRef, Table)| async move {
		let glob = if let Ok(s) = options.raw_get::<mlua::String>("glob") {
			Some(
				GlobBuilder::new(&s.to_str()?)
					.case_insensitive(true)
					.literal_separator(true)
					.backslash_escape(false)
					.empty_alternates(true)
					.build()
					.into_lua_err()?
					.compile_matcher(),
			)
		} else {
			None
		};

		let limit = options.raw_get("limit").unwrap_or(usize::MAX);
		let resolve = options.raw_get("resolve").unwrap_or(false);

		let mut it = match fs::read_dir(&*dir).await {
			Ok(it) => it,
			Err(e) => return (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
		};

		let mut files = vec![];
		while let Ok(Some(next)) = it.next_entry().await {
			if files.len() >= limit {
				break;
			}

			let path = next.path();
			if glob.as_ref().is_some_and(|g| !g.is_match(&path)) {
				continue;
			}

			let url = yazi_shared::url::Url::from(path);
			let file = if !resolve {
				yazi_fs::File::from_dummy(url, next.file_type().await.ok())
			} else if let Ok(meta) = next.metadata().await {
				yazi_fs::File::from_meta(url, meta).await
			} else {
				yazi_fs::File::from_dummy(url, next.file_type().await.ok())
			};
			files.push(File::cast(&lua, file)?);
		}

		let tbl = lua.create_table_with_capacity(files.len(), 0)?;
		for f in files {
			tbl.raw_push(f)?;
		}

		(tbl, Value::Nil).into_lua_multi(&lua)
	})
}

fn unique_name(lua: &Lua) -> mlua::Result<Function> {
	lua.create_async_function(|lua, url: UrlRef| async move {
		match yazi_fs::unique_name(url.clone(), async { false }).await {
			Ok(u) => (Url::cast(&lua, u)?, Value::Nil).into_lua_multi(&lua),
			Err(e) => (Value::Nil, Error::Io(e)).into_lua_multi(&lua),
		}
	})
}
  1. Use the listchars configuration above.
  2. Copy and paste the example file into Neovim.
  3. Observe that the tabs in the file are being displayed as leading spaces instead of the tab listchar character.

Expected Behavior

Tabs should be displayed with the tab listchar instead of the leading spaces listchar or the space listchar.

With indent-blankline.nvim:

image

Repro

vim.env.LAZY_STDPATH = ".repro"
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()

vim.opt.list = true
vim.opt.listchars = {
    lead = "·",
    trail = "",
    multispace = "",
    nbsp = "",
    tab = "⇥»",
    precedes = "",
    extends = "",
    eol = "",
    -- eol = "⤶",
}

require("lazy.minit").repro({
  spec = {
    { "folke/snacks.nvim", opts = {
      indent = { enabled = true },
    } },
    -- add any other plugins here
  },
})
@hankertrix hankertrix added the bug Something isn't working label Dec 17, 2024
@GunnerGuyven
Copy link

I am also experiencing this issue. With listchars set thus:

vim.opt.listchars = "tab:  ⇀,space:·"

Indent spacing appears as:

image

I expected to see my tab marker where I used a tab, and a space marker where a space actually is (not in the totally empty lines).

@GunnerGuyven
Copy link

I can confirm that my use case is corrected. Thank you very much!

@hankertrix
Copy link
Contributor Author

hankertrix commented Dec 18, 2024

I can also confirm that it is working great on my end!

image

I actually prefer this style over indent-blankline.nvim's style.

Thanks for the super quick fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants