Skip to content
New issue

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

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

Already on GitHub? # to your account

Using LuaJIT (FFI) #465

Open
qwerty12 opened this issue Jul 17, 2017 · 1 comment
Open

Using LuaJIT (FFI) #465

qwerty12 opened this issue Jul 17, 2017 · 1 comment

Comments

@qwerty12
Copy link

Hi,

I see the question has come up before in issue #111, which was closed, but I ask again not out of a desire for speed improvements but simply because LuaJIT's FFI module lets me address some small clink shortcomings and add small functionality without needing to recompile clink to add a new Lua-callable function.

For instance, clink.get_cwd() uses GetCurrentDirectoryA() which doesn't work well with umlauts (see #415) etc. I also like to have the Command Prompt's window title match the current directory I'm in, which saves me time when I have more than one cmd open. I know this can be done with doskey, a batch file and the built in title command, but I've found title requires certain paths to be wrapped in quotes -- which looks ugly. Meanwhile, SetConsoleTitleW just works without any hassle.

After switching out clink's Lua interpreter for LuaJIT-2.1.0-beta3, I have a working prompt script that does what I listed above (obtains the current directory path through GetCurrentDirectoryW() and sets the title):

clink_prompt.lua

local ffi = require("ffi")
ffi.cdef[[
int MultiByteToWideChar(unsigned int CodePage, unsigned int dwFlags, const char *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar);
int SetConsoleTitleW(const wchar_t *lpConsoleTitle);
unsigned int GetCurrentDirectoryW(unsigned int nBufferLength, wchar_t *lpBuffer);
int WideCharToMultiByte(unsigned int CodePage, unsigned int dwFlags, const wchar_t *lpWideCharStr, int cchWideChar, char *lpMultiByteStr, int cbMultiByte, const char *lpDefaultChar, bool *lpUsedDefaultChar);
]]
local C = ffi.C
local clink_prompt_home = os.getenv("USERPROFILE")

function set_title(title)
	local utf16_len = C.MultiByteToWideChar(65001, 0, title, -1, NULL, 0) --CP_UTF8
	if utf16_len > 0 then
		--utf16_len = utf16_len + 1
		local utf16_title = ffi.new("wchar_t[?]", utf16_len)
		if C.MultiByteToWideChar(65001, 0, title, -1, utf16_title, utf16_len) > 0 then
			C.SetConsoleTitleW(utf16_title)
		end
	end
end

function get_cwd_utf8()
	local path = ffi.new("wchar_t[?]", 260) --MAX_PATH

	if C.GetCurrentDirectoryW(260, path) ~= 0 then
		local utf8_size = C.WideCharToMultiByte(65001, 0, path, -1, NULL, 0, NULL, NULL)
		if utf8_size > 0 then
			local utf8_path = ffi.new("char[?]", utf8_size)
			local utf8_size = C.WideCharToMultiByte(65001, 0, path, -1, utf8_path, utf8_size, NULL, NULL)
			if utf8_size > 0 then
				return ffi.string(utf8_path, utf8_size)
			end
		end
	end

	return nil
end

function prompt()
	local new_prompt = nil
	local error_level = clink.get_env("=ExitCode")
	local cwd = get_cwd_utf8()

	if string.sub(cwd,1,string.len(clink_prompt_home))==clink_prompt_home then
		cwd = string.gsub(cwd, clink_prompt_home, '~')
		new_prompt = string.gsub(clink.prompt.value, clink_prompt_home, '~')
	end
	set_title(cwd)

	if error_level ~= "00000000" then
		new_prompt = "\x1b[1;31;40m" .. error_level:match("0*(%d+)") .. " " .. (new_prompt and new_prompt or clink.prompt.value)
	end

	if new_prompt ~= nil then
		clink.prompt.value = new_prompt
		return true
	end
end

clink.prompt.register_filter(prompt, 1)

If the excellent clink is picked up again, it would be great if I could use such scripts without needing to first compile clink myself.

Thanks for reading.

@chrisant996
Copy link

The 1.0.0a1 version uses GetCurrentDirectoryW, and then converts to UTF8 and sends the UTF8 back to LUA. It behaves similarly for other OS Unicode APIs as well.

The 1.0.0a1 version isn't in a very functional state, but the chrisant996/clink fork is getting it into working order and adding a bunch of features.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants