diff --git a/Assets/error.png b/Assets/error.png
new file mode 100644
index 0000000..9d4f19c
Binary files /dev/null and b/Assets/error.png differ
diff --git a/Assets/error.xcf b/Assets/error.xcf
new file mode 100644
index 0000000..a5a8635
Binary files /dev/null and b/Assets/error.xcf differ
diff --git a/DesignTime/CrashReportSampleData.fs b/DesignTime/CrashReportSampleData.fs
new file mode 100644
index 0000000..e1e2e94
--- /dev/null
+++ b/DesignTime/CrashReportSampleData.fs
@@ -0,0 +1,11 @@
+namespace FVim
+
+type CrashReportSampleData() =
+ member __.MainMessage = "Some error: some detailed error message.\nThis error is so bad that we have to bail out."
+ member __.TipMessage = "\n\nIn case of fire,\n 1. git commit\n 2. git push\n 3. run\nThank you for your cooperation."
+ member __.StackTrace = System.Collections.Generic.List [
+ "foo"
+ "bar"
+ "baz"
+ ]
+
diff --git a/Program.fs b/Program.fs
index a7e83eb..dc6ec90 100644
--- a/Program.fs
+++ b/Program.fs
@@ -64,15 +64,25 @@ let main(args: string[]) =
let _ = builder.SetupWithoutStarting()
// Avalonia is initialized. SynchronizationContext-reliant code should be working by now;
- try
- Model.Start opts
- with ex -> ()
- let cfg = config.load()
- let cwd = Environment.CurrentDirectory |> Path.GetFullPath
- let workspace = cfg.Workspace |> Array.tryFind(fun w -> w.Path = cwd)
- let mainwin = new MainWindowViewModel(workspace)
- lifetime.MainWindow <- MainWindow(DataContext = mainwin)
- let ret = lifetime.Start(args)
+ let model_start =
+ try
+ Model.Start opts
+ Ok()
+ with ex -> Error ex
- config.save cfg (int mainwin.X) (int mainwin.Y) mainwin.Width mainwin.Height mainwin.WindowState
- ret
+ match model_start with
+ | Ok() ->
+ let cfg = config.load()
+ let cwd = Environment.CurrentDirectory |> Path.GetFullPath
+ let workspace = cfg.Workspace |> Array.tryFind(fun w -> w.Path = cwd)
+ let mainwin = new MainWindowViewModel(workspace)
+ lifetime.MainWindow <- MainWindow(DataContext = mainwin)
+ let ret = lifetime.Start(args)
+
+ config.save cfg (int mainwin.X) (int mainwin.Y) mainwin.Width mainwin.Height mainwin.WindowState
+ ret
+ | Error ex ->
+ let crash = new CrashReportViewModel(ex)
+ lifetime.MainWindow <- new CrashReport(DataContext = crash)
+ ignore <| lifetime.Start(args)
+ -1
diff --git a/ViewModels/CrashReportViewModel.fs b/ViewModels/CrashReportViewModel.fs
new file mode 100644
index 0000000..d06f4f2
--- /dev/null
+++ b/ViewModels/CrashReportViewModel.fs
@@ -0,0 +1,23 @@
+namespace FVim
+
+open ui
+open log
+open common
+
+type CrashReportViewModel(ex: exn) =
+ inherit ViewModelBase()
+ member __.MainMessage =
+ ex.Message
+ member __.StackTrace =
+ ex.StackTrace.Split("\n")
+ member __.TipMessage =
+ let tip =
+ match ex.Message with
+ | "The system cannot find the file specified." -> "Tip: check your neovim installation. `nvim` is not in your $PATH.\n"
+ | _ -> ""
+ let generic_message =
+ "You can go to /~https://github.com/yatli/fvim/issues, and search\n" +
+ "for relevant issues with the exception message, or the stack trace.\n" +
+ "Feel free to create new issues, and I'll help to triage and fix the problem."
+ tip + generic_message
+
diff --git a/Views/CrashReport.xaml b/Views/CrashReport.xaml
new file mode 100644
index 0000000..b2fe505
--- /dev/null
+++ b/Views/CrashReport.xaml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Views/CrashReport.xaml.fs b/Views/CrashReport.xaml.fs
new file mode 100644
index 0000000..a78096a
--- /dev/null
+++ b/Views/CrashReport.xaml.fs
@@ -0,0 +1,13 @@
+namespace FVim
+
+open ui
+open log
+open common
+open Avalonia.Markup.Xaml
+open Avalonia.Controls
+
+type CrashReport() as this =
+ inherit Window()
+
+ do
+ AvaloniaXamlLoader.Load(this)
diff --git a/fvim.fsproj b/fvim.fsproj
index 63bc71d..39da925 100644
--- a/fvim.fsproj
+++ b/fvim.fsproj
@@ -25,6 +25,7 @@
+
@@ -41,12 +42,14 @@
+
+