-
Notifications
You must be signed in to change notification settings - Fork 725
Remove legacy drivers, simplify architecture, and reorganize codebase structure #4270
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
base: v2_develop
Are you sure you want to change the base?
Remove legacy drivers, simplify architecture, and reorganize codebase structure #4270
Conversation
Co-authored-by: tig <585482+tig@users.noreply.github.com>
Co-authored-by: tig <585482+tig@users.noreply.github.com>
Co-authored-by: tig <585482+tig@users.noreply.github.com>
Co-authored-by: tig <585482+tig@users.noreply.github.com>
Co-authored-by: tig <585482+tig@users.noreply.github.com>
CursesDriver
, NetDriver
, WindowsDriver
, and legacy Application
code and code that v2win/v2net
introduced that wraps that legacy
@copilot Unit tests are failing |
Co-authored-by: tig <585482+tig@users.noreply.github.com>
Fixed several test failures in commit a5e3b8c:
Key tests now passing: Init_NoParam_ForceDriver_Works, Run_t_Does_Not_Creates_Top_Without_Init Some tests may still fail due to the architectural changes from legacy drivers to v2 - these will need further investigation if they're critical. |
@copilot I've pushed some changes. Many unit tests fail; esp those that depend on FakeDriver. Before working on that, please move the V2Application tests that are in UnitTestse/ConsoleDrivers out of that folder into the proper place. |
@copilot In the current code, what is the distinction between the Application and Applicationv2 class? Why do both still exist? |
Co-authored-by: tig <585482+tig@users.noreply.github.com>
Moved all test files from Application tests (moved to
Driver tests (moved to
All namespaces updated and V2 subfolder removed. Build succeeds with 0 errors. |
The key connection: Both exist because:
The legacy drivers (CursesDriver, NetDriver, WindowsDriver) used ApplicationImpl directly, while the new architecture uses ApplicationV2 as the default. |
…g:gui-cs/Terminal.Gui into copilot-v2
@copilot In the current code, what is the distinction between the Application and Applicationv2 class? Why do both still exist? |
Indeed, hope op goes well and same for recovery! Yup I will take up this branch. I will probably try warp or Claude code locally with task to restore enough of old code in fluent tests as possible but working with the new names and changes of this pr |
Ok this fixes all the FluentTests, single line change. |
Theres a lot of 'knock on' failures, these ones seem to be consistently failing however: IntegrationTests.UICatalog.ScenarioTests.All_Scenarios_Quit_And_Init_Shutdown_Properly(scenarioType: typeof(UICatalog.Scenarios.RunTExample)) Failed IntegrationTests 508 ms Terminal.Gui.App.NotInitializedException : Run cannot be accessed before Initialization IntegrationTests.UICatalog.ScenarioTests.All_Scenarios_Quit_And_Init_Shutdown_Properly(scenarioType: typeof(UICatalog.Scenarios.SingleBackgroundWorker)) Failed Stale IntegrationTests 496 ms Terminal.Gui.App.NotInitializedException : Run cannot be accessed before Initialization StressTests.ScenariosStressTests.All_Scenarios_Benchmark(scenarioType: typeof(UICatalog.Scenarios.RunTExample)) Failed Stale StressTests 6 ms Terminal.Gui.App.NotInitializedException : Run cannot be accessed before Initialization StressTests.ScenariosStressTests.All_Scenarios_Benchmark(scenarioType: typeof(UICatalog.Scenarios.SingleBackgroundWorker)) Failed StressTests 449 ms Terminal.Gui.App.NotInitializedException : Run cannot be accessed before Initialization Looks like the above are related to |
Fixed in 14462ad The run generic was missing auto init. |
More test fixes here, theres a lot of tests that try to add to Likewise theres stuff that wants top initialized too seemingly at start of test - but that has to be done with Begin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR completes the migration to the modern driver architecture by removing all legacy v1 drivers (~4,000 lines), eliminating "v2" terminology, and reorganizing the codebase for better maintainability and separation of concerns.
Key changes include:
- Removed legacy drivers (
CursesDriver
,NetDriver
,WindowsDriver
) and their supporting infrastructure - Reorganized driver structure by moving files from
V2/
directory into clean, organized folders - Simplified driver names (removing "v2" prefix) and updated
Application.GetDriverTypes()
to return the new names
Reviewed Changes
Copilot reviewed 119 out of 148 changed files in this pull request and generated 2 comments.
Show a summary per file
File | Description |
---|---|
docfx/docs/drivers.md |
Updated documentation to reflect modern driver architecture |
Tests/UnitTests/**/*.cs |
Updated test files to use new driver names and modern FakeDriver interfaces |
Terminal.Gui/Drivers/WindowsDriver/WindowsKeyHelper.cs |
Extracted key conversion utilities from legacy WindowsDriver |
Terminal.Gui/Drivers/FakeDriver/*.cs |
Added modern FakeDriver implementation with component factory architecture |
Terminal.Gui/App/**/*.cs |
Moved platform-independent application infrastructure to separate folder |
Various driver files | Renamed classes/interfaces to remove "V2" terminology and updated driver names |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…g:gui-cs/Terminal.Gui into copilot-v2
…g:gui-cs/Terminal.Gui into copilot-v2
@tznind since the old arch no longer exists, how do you recommend we refactor ConsoleDriverFacade? |
Refactored `Application.Init` to improve initialization logic: - Added fallback to `ForceDriver` when `driverName` is null. - Changed repeated `Init` calls to throw `InvalidOperationException`. - Updated `_driverName` assignment logic for robustness. Enhanced `IConsoleDriver` with detailed remarks on implementations. Revised test cases to align with updated `Application.Init` behavior: - Replaced `FakeDriver` with `null` and `driverName: "fake"`. - Skipped or commented out tests incompatible with new logic. - Improved formatting and removed redundant setup code. Improved code style and consistency across the codebase: - Standardized parameter formatting and spacing. - Removed outdated comments and unused code. General cleanup to enhance readability and maintainability.
hmn ultimately I would like to see us get rid of Driver and static Application too. What we really want is var f = new ApplicationFactory();
using IApplication a = f.Create("v2net");
a.Run(new View()); MainLoopCoordinator is my attempt to do that - a self contained application class that holds all the sub component interfaces (out, buffer, timed events, main loop etc). I added Here is Warp Terminal's summary of code that Views currently deal with 1. Static Application Class (Heavy Usage)// Application lifecycle
Application.Init()
Application.Run<T>()
Application.Run(view)
Application.Shutdown()
Application.RequestStop()
// Application state
Application.Top
Application.Driver
Application.Screen
Application.Navigation
Application.Popover
Application.QuitKey
Application.MainThreadId
Application.MouseGrabHandler
// Event coordination
Application.LayoutAndDraw()
Application.RaiseKeyDownEvent()
Application.RaiseKeyUpEvent()
Application.Invoke()
// Timeout management
Application.AddTimeout()
Application.RemoveTimeout()
Application.TimedEvents 2. IConsoleDriver Interface (ConsoleDriverFacade)// Drawing operations (most common)
Driver.AddRune()
Driver.AddStr()
Driver.Move()
Driver.FillRect()
Driver.ClearContents()
// State properties
Driver.Col, Driver.Row
Driver.Cols, Driver.Rows
Driver.Contents
Driver.CurrentAttribute
Driver.Clip
Driver.Screen
// Cursor operations
Driver.UpdateCursor()
Driver.SetCursorVisibility()
Driver.GetCursorVisibility()
// Other operations
Driver.WriteRaw()
Driver.QueueAnsiRequest()
Driver.GetRequestScheduler()
Driver.Clipboard
Driver.IsRuneSupported()
Driver.IsValidLocation()
Driver.MakeColor() 3. Application-Level Navigation & Focus// Cross-view navigation (external dependency)
Application.Navigation.GetFocused()
SetFocus() // when it crosses view boundaries
// Mouse grab coordination (global state)
Application.MouseGrabHandler.MouseGrabView
Application.MouseGrabHandler.UngrabMouse() 4. Global Event Coordination// Application-level events that Views subscribe to
Application.KeyDown
Application.KeyUp
Application.MouseEvent
Application.SizeChanging
// Cross-view event raising
Application.RaiseKeyDownEvent()
Application.RaiseKeyUpEvent() 5. Layout Coordination (Cross-View)// Global layout orchestration
Application.LayoutAndDraw() // forces redraw of entire app
Application.Screen // global screen bounds
// Only the cross-view coordinate transformation parts:
FrameToScreen() // when crossing view boundaries
ViewportToScreen() // when crossing view boundaries 6. Platform Services// Platform clipboard (external system)
Application.Driver.Clipboard
Clipboard.GetClipboardData()
Clipboard.SetClipboardData()
// Configuration system (external)
ConfigurationManager.Enable()
Configuration attributes and loading Key Observations:
This means elimination of both static Application AND ConsoleDriverFacade affects:
The architectural change is significant but more focused - mainly about dependency injection and removing static global state, rather than rewriting all View functionality. Proposed Refactoring PlanStep 1: Rename & Expose MainLoopCoordinator// Rename MainLoopCoordinator -> Application
public class Application<T> : IApplication, IDisposable
{ Step 4: Migration Strategy
Benefits After Refactoring✅ No Static Global State - Everything is instance-based The Bottom LineMainLoopCoordinator already IS the application architecture we want. We just need to:
This approach leverages the existing solid architecture rather than creating something entirely new. |
This is really good stuff and I'm mostly aligned with it. I think the first step is to get this PR back to the point where all unit tests are passing. That should be done by hacking iCconsoleDriver to let existing code work. For example, either iconsole, Driver or the façade class needs to expose something that says which driver is which. So that the test code that does get type can be ported. I started going down this path this morning, but then realized that the façade was in the way. Now, based on your comments, I see I was right and I should've continued to just hack away at it to make it work in a temporary manner. |
Indeed yes, the v2 architecture is designed for exactly this - incremental delivery. I will find some time to take another look at the remaining failing tests - It seemed mostly around gnarly main loop manual iteration and timing stuff. |
…g:gui-cs/Terminal.Gui into copilot-v2
…g:gui-cs/Terminal.Gui into copilot-v2
Updated `FakeDriver` to set `RunningUnitTests` to `true` and initialize dimensions using `FakeConsole`. Modified `TestRespondersDisposedAttribute` to set `ConsoleDriver.RunningUnitTests` in the `Before` method, ensuring proper behavior during unit tests. Temporarily disabled the `Button_CanFocus_False_Raises_Accepted_Correctly` test in `ViewCommandTests` by adding a `Skip` parameter to the `[Fact]` attribute, referencing issue #4270.
@tznind Just pushed some fixes that enable all unit tests to pass reliably locally, but still seeing CI/CD failures. There's some weridness in there still for sure, and I'm eager to get this merged so we can move on. I can't see why the non-par tests are failing in CI/CD. Frustrating. I'll keep poking at it, but keyboarding continues to be painful. 😒 |
Modified the `dotnet test` command in the `Run UnitTestsParallelizable` step to set `xunit.stopOnFail` to `false`. This ensures that the test runner does not stop execution on the first failure, allowing all tests to execute regardless of individual test outcomes.
Refactored `ClearContents_Called_When_Top_Frame_Changes` test: - Added `[AutoInitShutdown]` attribute for automatic lifecycle management. - Replaced manual `Application.Init` and `Application.Top` setup with `Application.Begin` and `RunState`. - Simplified event handling by defining `ClearedContents` handler inline. - Removed explicit cleanup logic, relying on `Application.End` for teardown. Updated `using` directives to include `UnitTests` namespace.
All passed! I'm skeptcial tho. |
The reason why the |
Update ApplicationImpl initialization parameter Changed the second parameter of the `impl.Init` method in the `FakeApplicationFactory` class from `"dotnet"` to `"fake"`.
This PR completes the migration to the modern driver architecture by removing all legacy v1 drivers, eliminating "v2" terminology, and reorganizing the codebase for better maintainability and separation of concerns.
Removed Legacy Drivers (~4,000 lines)
Deleted the following legacy driver directories and all supporting code:
Terminal.Gui/Drivers/CursesDriver/
- ncurses-based driver with Unix bindingsTerminal.Gui/Drivers/NetDriver/
- Legacy .NET Console driverTerminal.Gui/Drivers/WindowsDriver/
- Legacy Windows Console API driverUnixMainLoop
,NetMainLoop
,WindowsMainLoop
binding.cs
,constants.cs
,handles.cs
)Reorganized Driver Structure
Moved all files from the
V2/
directory into a clean, organized structure:Simplified Driver Names
Driver names no longer use "v2" prefix:
"v2net"
→"dotnet"
"v2win"
→"windows"
"v2unix"
→"unix"
Updated
Application.GetDriverTypes()
to return the new names.Architecture Simplification
Merged Application Implementations
ModernApplicationImpl.cs
(previouslyApplicationV2.cs
)ApplicationImpl.cs
in theApp
folderSeparated Platform-Independent from Platform-Specific Code
Reorganized codebase to clearly separate concerns:
Moved to
/App
(platform-independent):/App/MainLoop/
- Main loop orchestration (ApplicationMainLoop<T>
,MainLoopCoordinator
, interfaces)/App/Lifecycle/
- View lifecycle management (ToplevelTransitionManager
, interface)/App/NotInitializedException.cs
- Application exception typeRemains in
/Drivers
(platform-specific):DotNetDriver
,WindowsDriver
,UnixDriver
,FakeDriver
)IConsoleInput
,IConsoleOutput
,IComponentFactory
, etc.)Platform.cs
,PlatformDetection.cs
)InputProcessor
,OutputBase
,OutputBuffer
)Clarified MainLoop Architecture
Renamed for clarity:
MainLoop.cs
→LegacyMainLoopDriver.cs
(marked obsolete, for FakeDriver compatibility)MainLoop<T>
→ApplicationMainLoop<T>
(the modern main application loop)IMainLoop<T>
→IApplicationMainLoop<T>
(interface for main loop)Result:
Removed V2 Terminology
Renamed all classes and types to remove "V2" terminology:
Core Classes:
ApplicationV2
→ApplicationImpl
(merged)FakeDriverV2
→FakeConsoleDriver
IFakeDriverV2
→IFakeConsoleDriver
Test Infrastructure:
V2TestDriver
→TestDriver
V2TestDrivers
→TestDrivers
V2Win
→Windows
,V2Net
→DotNet
Modern FakeDriver Implementation
Created a proper modern FakeDriver using the component factory architecture in
Terminal.Gui/Drivers/FakeDriver/
:New Files:
FakeComponentFactory.cs
- Factory for creating fake console componentsFakeConsoleInput.cs
- Fake input that extendsConsoleInput<ConsoleKeyInfo>
and supports predefined inputFakeConsoleOutput.cs
- Fake output that extendsOutputBase
and captures console output for verificationArchitecture:
IComponentFactory<ConsoleKeyInfo>
pattern like DotNetDriverThe legacy FakeDriver remains in place temporarily for backward compatibility with existing tests.
FakeDriver Testing - All Tests Passing ✅
Added comprehensive test suite for FakeDriver in
Tests/UnitTests/ConsoleDrivers/FakeDriverTests.cs
:Fixed issues:
Console.MockKeyPresses
and hung indefinitely with modern architectureResult: All 21 FakeDriver tests pass, plus 3 ConsoleDriverTests pass without hanging!
Removed Hanging Tests & Added Proper Coverage
Removed the following tests from ConsoleDriverTests.cs because they:
Console.MockKeyPresses
) incompatible with modern architectureApplication.Run()
loopsRemoved tests:
FakeDriver_MockKeyPresses
FakeDriver_Only_Sends_Keystrokes_Through_MockKeyPresses
FakeDriver_IsValidInput_Wrong_Surrogate_Sequence
FakeDriver_IsValidInput_Correct_Surrogate_Sequence
Added replacement tests in NetInputProcessorTests.cs:
ProcessInput_Handles_Valid_Surrogate_Pair
- Tests valid high+low surrogate sequencesProcessInput_Handles_Invalid_High_Surrogate_Without_Low
- Tests incomplete high surrogateProcessInput_Handles_Invalid_Low_Surrogate_Without_High
- Tests orphaned low surrogateProcessInput_Handles_Invalid_Reversed_Surrogates
- Tests reversed surrogate orderThe new tests properly validate UTF-16 surrogate pair handling in the input processor layer where this logic actually resides.
Test Organization
Moved test files from
Tests/UnitTests/ConsoleDrivers/V2/
to appropriate locations:Moved to Application folder:
ApplicationV2Tests.cs
→ApplicationImplTests.cs
MainLoopCoordinatorTests.cs
MainLoopTTests.cs
Moved to ConsoleDrivers folder:
ConsoleInputTests.cs
MouseInterpreterTests.cs
NetInputProcessorTests.cs
WindowSizeMonitorTests.cs
WindowsInputProcessorTests.cs
Extracted Utilities
Created helper classes to support the reorganization:
WindowsKeyHelper
- Static key conversion utilities extracted from legacy WindowsDriverPlatformDetection
- WSL and platform detection utilitiesUpdated Documentation
docfx/docs/drivers.md
- Removed legacy driver documentation, added modern driver infoApplication.Driver.cs
,ApplicationImpl.cs
,Application.Initialization.cs
Breaking Changes
For code using legacy drivers directly:
new CursesDriver()
,new NetDriver()
,new WindowsDriver()
will no longer compileApplication.ForceDriver = "dotnet"/"windows"/"unix"
insteadFor code using v2 driver names:
"v2net"
→"dotnet"
"v2win"
→"windows"
"v2unix"
→"unix"
Most applications using
Application.Init()
without specifying a driver will be unaffected - the framework automatically selects the appropriate driver for the platform.Testing
Summary
This PR removes ~4,000 lines of legacy code, eliminates all "V2" terminology, clarifies the MainLoop architecture with better naming and documentation, and creates a cleaner separation between platform-independent application infrastructure (
/App
) and platform-specific driver implementations (/Drivers
). The modern component factory architecture is now the only architecture, with three production drivers (dotnet, windows, unix) and a fully functional modern fake driver for testing.Fixes #4245
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.