Skip to content

Conversation

lammel
Copy link
Collaborator

@lammel lammel commented Jun 19, 2023

With this merge request using a custom writer is now possible by using a instanced cursor area.
Moving the cursor will be restricted to the size of the area. Multiline is also supported.

Marked as draft, as tests have only been conducted under Linux and probably some discussion is needed.

@lammel lammel force-pushed the feature/custom-writer branch from 15981f0 to 83eb5c3 Compare June 19, 2023 22:40
@MarvinJWendt
Copy link
Member

Hi, awesome PR, thanks!

I am unable to compile it under Linux currently:
image

I like the idea, and I think implementation wise, there is not much to discuss (looks good to me).

Copy link
Member

@MarvinJWendt MarvinJWendt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that you do not have a Windows environment? If so, what would be the most appropriate way for you to implement it for Windows? Let me know if you need any help with that :)

@lammel
Copy link
Collaborator Author

lammel commented Jun 20, 2023

Hi, awesome PR, thanks!

I am unable to compile it under Linux currently: image

I like the idea, and I think implementation wise, there is not much to discuss (looks good to me).

OMG! I built my application with clil based on pterm with interactive text input and multiline text-input over StdErr yesterday. Still wonder how I managed to push a broken version :)

I assume that you do not have a Windows environment? If so, what would be the most appropriate way for you to implement it for Windows? Let me know if you need any help with that :)

I have a setup for Windows 10 with Powershell running in a VM, so probably some first steps are possible.
But a little help to verify this PR would be very welcome.

@MarvinJWendt
Copy link
Member

OMG! I built my application with clil based on pterm with interactive text input and multiline text-input over StdErr yesterday. Still wonder how I managed to push a broken version :)

Haha no worries ^^

I have a setup for Windows 10 with Powershell running in a VM, so probably some first steps are possible.
But a little help to verify this PR would be very welcome.

Sounds good. If you're stuck at some point, give me a ping :)

@lammel lammel requested a review from MarvinJWendt June 20, 2023 20:51
@lammel lammel marked this pull request as ready for review June 21, 2023 21:41
@codecov
Copy link

codecov bot commented Jun 26, 2023

Codecov Report

Merging #17 (1286808) into main (00834e0) will increase coverage by 1.01%.
The diff coverage is 10.00%.

@@           Coverage Diff            @@
##            main     #17      +/-   ##
========================================
+ Coverage   3.84%   4.86%   +1.01%     
========================================
  Files          4       6       +2     
  Lines        130     185      +55     
========================================
+ Hits           5       9       +4     
- Misses       124     176      +52     
+ Partials       1       0       -1     
Files Changed Coverage Δ
area.go 0.00% <0.00%> (ø)
area_other.go 0.00% <0.00%> (ø)
cursor.go 0.00% <0.00%> (-33.34%) ⬇️
cursor_other.go 28.57% <28.57%> (ø)
utils.go 13.15% <31.25%> (+13.15%) ⬆️

Copy link
Member

@MarvinJWendt MarvinJWendt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, I have left some comments.

Just for reference:

We need to use the internal Writer interface everywhere, instead of io.Writer. Windows handles cursor movement via syscalls, which means we need a file descriptor. If you would do this:

func main() {
	var buffer bytes.Buffer
	a := cursor.NewArea().WithWriter(&buffer)
	a.Update("Hello World!")
}

We would get this panic on Windows:
image

We have to do this for every operating system, as otherwise, Linux / macOS users would be able to do the example above, which would then panic on Windows. By accepting the internal Writer interface, we make sure that there has to be a file descriptor.

The only downside to this is, that the area cannot write to a buffer, but that isn't really relevant, as there is no cursor to move in a buffer anyway.

The rest looks good to me :)

// PS:

Sorry for the delay, I was on vacation.

@lammel
Copy link
Collaborator Author

lammel commented Jun 27, 2023

Hi, I have left some comments.

Just for reference:

We need to use the internal Writer interface everywhere, instead of io.Writer. Windows handles cursor movement via syscalls, which means we need a file descriptor.

Done. pterm also requires a PR to make use of cursor.WithWriter which will be handled by a followup PR.

The rest looks good to me :)

Great!

Sorry for the delay, I was on vacation.

Nice, will do the same next week. So in case we haven't merged then, we might need some more days then 🆗

@lammel
Copy link
Collaborator Author

lammel commented Jul 3, 2023

I ran the area tests on my virtualized Windows 10 VM with good results so far.
Behaves exactly the same on Linux and Windows.

But would be good if you give it a try. Thanks for the support here.

@lammel lammel requested a review from MarvinJWendt July 15, 2023 21:53
@lammel
Copy link
Collaborator Author

lammel commented Jul 17, 2023

Just fixed the compile error on windows, caused by a missing commit.
Please let the workflow run again (PR for CI lint is also available now)

@MarvinJWendt
Copy link
Member

MarvinJWendt commented Jul 18, 2023

Hi @lammel, sorry for the late response, I was on vacation.
I have implemented an important fix that caused the area to be messy on Windows. Could you please merge the changes of #20 into this PR? Thanks! (On Update Windows should use Fprint and everything else Fprintln and print line by line)

// Update overwrites the content of the Area.
func (area *Area) Update(content string) {
	oldWriter := target

	SetTarget(area.writer) // Temporary set the target to the Area's writer so we can use the cursor functions
	area.Clear()
	lines := strings.Split(content, "\n")
	fmt.Fprintln(area.writer, strings.Repeat("\n", len(lines)-1)) // This appends space if the terminal is at the bottom
	Up(len(lines))
	SetTarget(oldWriter) // Reset the target to the old writer

	// Workaround for buggy behavior on Windows
	if runtime.GOOS == "windows" {
		for _, line := range lines {
			fmt.Fprint(area.writer, line)
			StartOfLineDown(1)
		}
	} else {
		for _, line := range lines {
			fmt.Fprintln(area.writer, line)
		}
	}

	height = 0
	area.height = len(strings.Split(content, "\n"))
}

@MarvinJWendt
Copy link
Member

Hi @lammel, there are some linting errors, and they (finally) are reported correctly. You can see them here: https://github.com/atomicgo/cursor/pull/17/files

Or if you have docker installed, you can use this command locally: docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint:v1.53.1 golangci-lint run.

Btw: You don't need to update the README.md files in AtomicGo packages. They are generated automatically when the PR is pushed to master 😉

@lammel
Copy link
Collaborator Author

lammel commented Jul 19, 2023

Hi @lammel, there are some linting errors, and they (finally) are reported correctly. You can see them here: https://github.com/atomicgo/cursor/pull/17/files

Or if you have docker installed, you can use this command locally: docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint:v1.53.1 golangci-lint run.

I'm using a locally installed golangci-lint now.
The rules seem a little too harsh (exhaustive struct, cuddled blocks), but I've fixed those linter warnings now.

Btw: You don't need to update the README.md files in AtomicGo packages. They are generated automatically when the PR is pushed to master wink

Thanks for noting, good to know and saves some time :)

@lammel
Copy link
Collaborator Author

lammel commented Jul 19, 2023

Hi @lammel, sorry for the late response, I was on vacation. I have implemented an important fix that caused the area to be messy on Windows. Could you please merge the changes of #20 into this PR? Thanks! (On Update Windows should use Fprint and everything else Fprintln and print line by line)

// Update overwrites the content of the Area.
func (area *Area) Update(content string) {
	oldWriter := target

	SetTarget(area.writer) // Temporary set the target to the Area's writer so we can use the cursor functions
	area.Clear()
	lines := strings.Split(content, "\n")
	fmt.Fprintln(area.writer, strings.Repeat("\n", len(lines)-1)) // This appends space if the terminal is at the bottom
	Up(len(lines))
	SetTarget(oldWriter) // Reset the target to the old writer

	// Workaround for buggy behavior on Windows
	if runtime.GOOS == "windows" {
		for _, line := range lines {
			fmt.Fprint(area.writer, line)
			StartOfLineDown(1)
		}
	} else {
		for _, line := range lines {
			fmt.Fprintln(area.writer, line)
		}
	}

	height = 0
	area.height = len(strings.Split(content, "\n"))
}

Actually I removed the newline for linux on purpose, as single line updates would otherwise leak into the next line, although this is not required and requires more cursor movement.
So I'd like to keep the minimal cursor movement and fix the cursor/area usage in pterm.
Or do you see compatibility issues for other software using cursor?

@MarvinJWendt
Copy link
Member

MarvinJWendt commented Jul 22, 2023

I think that should work, the linux implementation didn't had any bugs. For Windows the StartOfLineDown(1) part is important. Otherwise, it would result in this:

idea64_uDtapqB6WY.mp4

Also, I couldn't reproduce the bug on Windows consistently. The select and multiselect printers were affected all the time, but I couldn't really isolate the problem. Sometimes the Windows terminal is just a little weird.

@lammel lammel force-pushed the feature/custom-writer branch from d5f4f59 to c886465 Compare July 28, 2023 00:01
@lammel
Copy link
Collaborator Author

lammel commented Jul 28, 2023

I think that should work, the linux implementation didn't had any bugs. For Windows the StartOfLineDown(1) part is important. Otherwise, it would result in this:

idea64_uDtapqB6WY.mp4
Also, I couldn't reproduce the bug on Windows consistently. The select and multiselect printers were affected all the time, but I couldn't really isolate the problem. Sometimes the Windows terminal is just a little weird.

As this is a pterm test that might also cause unexpected behaviour I've added some tests under _examples here now to reproduce the issues.

I could reproduce 1 windows issue and one generic issue you were facing.
For windows the cursor package now ensures \r\n is used for area.Update() which fixes the cursor not moving to the start of the line. This is done in a platform dependent helper in area_windows.go now. Furthermore this problem will probably be resolved by future Windows updates (hopefully).

The second issue was reproduced by _example/movement/main.go which showed a jumpy behavior of the area in case content length differs (e.g. when searching in pterms interactive_select` example)

Both of these issues are resolved now. The only remaining problem seems to be moving the cursor Up() out of bounds of the area, which causes the next update to garble output.

Your tests with pterm should run a lot smoother now.

@MarvinJWendt
Copy link
Member

Looks great!

Both of these issues are resolved now. The only remaining problem seems to be moving the cursor Up() out of bounds of the area, which causes the next update to garble output.

Could this be fixed by preventing to call Up() when the cursor is in line 1 of the area?

@lammel
Copy link
Collaborator Author

lammel commented Jul 28, 2023

Both of these issues are resolved now. The only remaining problem seems to be moving the cursor Up() out of bounds of the area, which causes the next update to garble output.

Could this be fixed by preventing to call Up() when the cursor is in line 1 of the area?

Should be, just ran out of time yesterday, I'll look into it soon.
Once merged this should allow pterm to have a quite reliable cursor area for the interactive inputs which also handle single line inputs.

@lammel
Copy link
Collaborator Author

lammel commented Jul 28, 2023

After making the linter happy again, the above issues are fixed now.

So only the pterms multiline input has issues with the custom cursor positioning,
The y-axis for cursorPosY is a little awkward, I'd suggest to correct it to 0x0 being the upper left (not lower left) of the terminal area (already that way in pterm). Any objections?

@lammel
Copy link
Collaborator Author

lammel commented Jul 28, 2023

This PR is now ready to merge. All added examples here and for pterm work as expected on Linux and Windows.
For pterm please be sure to test with PR pterm/pterm#529

@lammel
Copy link
Collaborator Author

lammel commented Jul 28, 2023

Ah one more note... This PR may break current behavior depending on the use of the cursor area. You might consider that when merging/releasing.

@MarvinJWendt
Copy link
Member

After making the linter happy again, the above issues are fixed now.

So only the pterms multiline input has issues with the custom cursor positioning, The y-axis for cursorPosY is a little awkward, I'd suggest to correct it to 0x0 being the upper left (not lower left) of the terminal area (already that way in pterm). Any objections?

Just to clarify, you mean the position in the Area right? If so, no objections from my side :)

Copy link
Member

@MarvinJWendt MarvinJWendt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for contributing 🚀

PS: Your examples look awesome!

@MarvinJWendt MarvinJWendt merged commit 0754e36 into atomicgo:main Aug 1, 2023
@MarvinJWendt
Copy link
Member

Quick addition, I have invited you to this repo as collaborator (which gives you some more rights). If you want, you can accept the invitation here: https://github.com/atomicgo/cursor/invitations.

Don't feel pressured by this to do more, but take it as appreciation :)

@lammel
Copy link
Collaborator Author

lammel commented Aug 1, 2023

Quick addition, I have invited you to this repo as collaborator (which gives you some more rights). If you want, you can accept the invitation here: https://github.com/atomicgo/cursor/invitations.

Don't feel pressured by this to do more, but take it as appreciation :)

Thanks! I feel a little pressured ;-)
But I'm already looking into adding missing pieces to pterm (cursor here was just a requirement to get Stderr working there), so why not help out a little more 🆗

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

Successfully merging this pull request may close these issues.

2 participants