Merge branch 'master' into double_buffer
add env variable to enable double buffering NVIM_GTK_DOUBLE_BUFFER
This commit is contained in:
commit
83ca7bf342
276
Cargo.lock
generated
276
Cargo.lock
generated
@ -8,12 +8,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atk-sys"
|
name = "atk-sys"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
@ -50,25 +50,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cairo-rs"
|
name = "cairo-rs"
|
||||||
version = "0.3.0"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"c_vec 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"c_vec 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cairo-sys-rs"
|
name = "cairo-sys-rs"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -118,103 +117,106 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gdk"
|
name = "gdk"
|
||||||
version = "0.7.0"
|
version = "0.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cairo-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cairo-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gdk-pixbuf 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdk-pixbuf 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gdk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gio 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gio 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pango 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gdk-pixbuf"
|
name = "gdk-pixbuf"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gdk-pixbuf-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdk-pixbuf-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gio 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gdk-pixbuf-sys"
|
name = "gdk-pixbuf-sys"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gio-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gdk-sys"
|
name = "gdk-sys"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gdk-pixbuf-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdk-pixbuf-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gio-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gio"
|
name = "gio"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gio-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gio-sys"
|
name = "gio-sys"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glib"
|
name = "glib"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glib-sys"
|
name = "glib-sys"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -224,52 +226,53 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gobject-sys"
|
name = "gobject-sys"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gtk"
|
name = "gtk"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cairo-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cairo-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gdk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdk 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gdk-pixbuf 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdk-pixbuf 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gdk-pixbuf-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdk-pixbuf-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gdk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gio 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gio 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gio-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gtk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gtk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pango 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gtk-sys"
|
name = "gtk-sys"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"atk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gdk-pixbuf-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdk-pixbuf-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gdk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gio-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -364,86 +367,96 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
name = "nvim-gtk"
|
name = "nvim-gtk"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cairo-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cairo-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"env_logger 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"env_logger 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gdk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdk 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gdk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gdk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gio 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gio 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gtk 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gtk 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gtk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gtk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"neovim-lib 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"neovim-lib 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pango 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pangocairo 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pangocairo 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pangocairo-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pangocairo-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
"phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
"phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rmpv 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pango"
|
name = "pango"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pango-sys"
|
name = "pango-sys"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pangocairo"
|
name = "pangocairo"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cairo-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cairo-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pango 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pangocairo-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pangocairo-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pangocairo-sys"
|
name = "pangocairo-sys"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "percent-encoding"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.7.21"
|
version = "0.7.21"
|
||||||
@ -669,6 +682,11 @@ dependencies = [
|
|||||||
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.0.4"
|
version = "0.0.4"
|
||||||
@ -701,11 +719,6 @@ name = "void"
|
|||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi"
|
|
||||||
version = "0.2.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
@ -735,31 +748,31 @@ dependencies = [
|
|||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
|
"checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
|
||||||
"checksum atk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "33a67fd81e1922dddc335887516f2f5254534e89c9d39fa89bca5d79bd150d34"
|
"checksum atk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8dc233521f7bffd3042c31082ea71bd08820abf44bac938fb36591e20f76f39"
|
||||||
"checksum atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8352656fd42c30a0c3c89d26dea01e3b77c0ab2af18230835c15e2e13cd51859"
|
"checksum atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8352656fd42c30a0c3c89d26dea01e3b77c0ab2af18230835c15e2e13cd51859"
|
||||||
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
|
"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5"
|
||||||
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
|
"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf"
|
||||||
"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
|
"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23"
|
||||||
"checksum c_vec 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6237ac5a4b1e81c213c24c6437964c61e646df910a914b4ab1487b46df20bd13"
|
"checksum c_vec 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6237ac5a4b1e81c213c24c6437964c61e646df910a914b4ab1487b46df20bd13"
|
||||||
"checksum cairo-rs 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6b5695f59fd036fe5741bc5a4eb20c78fbe42256e3b08a2af26bbcbe8070bf3"
|
"checksum cairo-rs 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a110f269c2fd382df5fe8bd46dfa5f1b83608aa717fecb6e7a28c08c202f0e13"
|
||||||
"checksum cairo-sys-rs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c6e18fecaeac51809db57f45f4553cc0975225a7eb435a7a7e91e5e8113a84d"
|
"checksum cairo-sys-rs 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0395175ecba60accac076a02c31d143b9dcd9d5eb5316d7163a3273803b765c7"
|
||||||
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
|
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
|
||||||
"checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9"
|
"checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9"
|
||||||
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
|
"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab"
|
||||||
"checksum env_logger 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f15f0b172cb4f52ed5dbf47f774a387cd2315d1bf7894ab5af9b083ae27efa5a"
|
"checksum env_logger 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f15f0b172cb4f52ed5dbf47f774a387cd2315d1bf7894ab5af9b083ae27efa5a"
|
||||||
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
||||||
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||||
"checksum gdk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e51db95be6565011bcd5cd99f9b17fdd585001057a999b21e09f1e8c28deb9"
|
"checksum gdk 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd30051ff3d908ff2fc7e5776ffe1c699821e043809f294c3a61004f11d6c3a9"
|
||||||
"checksum gdk-pixbuf 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "16160d212ae91abe9f3324c3fb233929ba322dde63585d15cda3336f8c529ed1"
|
"checksum gdk-pixbuf 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c2d2199eba47ebcb9977ce28179649bdd59305ef465c4e6f9b65aaa41c24e6b5"
|
||||||
"checksum gdk-pixbuf-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "798f97101eea8180da363d0e80e07ec7ec6d1809306601c0100c1de5bc8b4f52"
|
"checksum gdk-pixbuf-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df6a3b73e04fafc07f5ebc083f1096a773412e627828e1103a55e921f81187d8"
|
||||||
"checksum gdk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4ee916f5f25c5f4b21bd9dcb12a216ae697406940ff9476358c308a8ececada"
|
"checksum gdk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3162ff940526ddff71bf1f630facee6b5e05d282d125ba0c4c803842819b80c3"
|
||||||
"checksum gio 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "84ba5a2beb559059a0c9c2bd3681743cdede8d9a36c775840bca800333b22867"
|
"checksum gio 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "071fd4e5592b39fdc31318e32d2497842501f539bcedb60c75cd365946642adc"
|
||||||
"checksum gio-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a303bbf7a5e75ab3b627117ff10e495d1b9e97e1d68966285ac2b1f6270091bc"
|
"checksum gio-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a57872499171d279f8577ce83837da4cae62b08dd32892236ed67ab7ea61030"
|
||||||
"checksum glib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9b0452824cc63066940f01adc721804919f0b76cdba3cfab977b00b87f16d4a"
|
"checksum glib 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e0be1b1432e227bcd1a9b28db9dc1474a7e7fd4227e08e16f35304f32d09b61"
|
||||||
"checksum glib-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9693049613ff52b93013cc3d2590366d8e530366d288438724b73f6c7dc4be8"
|
"checksum glib-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615bef979b5838526aee99241afc80cfb2e34a8735d4bcb8ec6072598c18a408"
|
||||||
"checksum gobject-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60d507c87a71b1143c66ed21a969be9b99a76df234b342d733e787e6c9c7d7c2"
|
"checksum gobject-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "70409d6405db8b1591602fcd0cbe8af52cd9976dd39194442b4c149ba343f86d"
|
||||||
"checksum gtk 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0847c507e52c1feaede13ef56fb4847742438602655449d5f1f782e8633f146f"
|
"checksum gtk 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "414f3522f550a0b4f65e089f00ffcd3987dab8b0be284cb979aa7f6a03d60516"
|
||||||
"checksum gtk-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "905fcfbaaad1b44ec0b4bba9e4d527d728284c62bc2ba41fccedace2b096766f"
|
"checksum gtk-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d9554cf5b3a85a13fb39258c65b04b262989c1d7a758f8f555b77a478621a91"
|
||||||
"checksum htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163"
|
"checksum htmlescape 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163"
|
||||||
"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
|
"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c"
|
||||||
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
|
"checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d"
|
||||||
@ -772,10 +785,11 @@ dependencies = [
|
|||||||
"checksum num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "4b226df12c5a59b63569dd57fafb926d91b385dfce33d8074a412411b689d593"
|
"checksum num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "4b226df12c5a59b63569dd57fafb926d91b385dfce33d8074a412411b689d593"
|
||||||
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
|
"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
|
||||||
"checksum num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7de20f146db9d920c45ee8ed8f71681fd9ade71909b48c3acbd766aa504cf10"
|
"checksum num-traits 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e7de20f146db9d920c45ee8ed8f71681fd9ade71909b48c3acbd766aa504cf10"
|
||||||
"checksum pango 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e81c404ab81ea7ea2fc2431a0a7672507b80e4b8bf4b41eac3fc83cc665104e"
|
"checksum pango 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45374801e224373c3c0393cd48073c81093494c8735721e81d1dbaa4096b2767"
|
||||||
"checksum pango-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34f34a1be107fe16abb2744e0e206bee4b3b07460b5fddd3009a6aaf60bd69ab"
|
"checksum pango-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94039b3921a4af4058a3e4335e5d15099101f298a92f5afc40bab3a3027594a1"
|
||||||
"checksum pangocairo 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "41a8620ece55098d741bacf4d3aa52398f85ce83cfe0d8f670fa11de88f52c40"
|
"checksum pangocairo 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db4130d0e0567b177178c9568466277ff167cb43a245b2881e203509ea5fbd84"
|
||||||
"checksum pangocairo-sys 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e34ec010b38a2a6dafdf3d86ffe1251a0ae759208ec85f78c66c0445481bd5a8"
|
"checksum pangocairo-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "decc9523cd5bbba49a67ed8d2ef7dfca957f4760bf420a1ea4a82634da26381c"
|
||||||
|
"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
|
||||||
"checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc"
|
"checksum phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "cb325642290f28ee14d8c6201159949a872f220c62af6e110a56ea914fbe42fc"
|
||||||
"checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f"
|
"checksum phf_codegen 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d62594c0bb54c464f633175d502038177e90309daf2e0158be42ed5f023ce88f"
|
||||||
"checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"
|
"checksum phf_generator 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)" = "6b07ffcc532ccc85e3afc45865469bf5d9e4ef5bfcf9622e3cfe80c2d275ec03"
|
||||||
@ -803,12 +817,12 @@ dependencies = [
|
|||||||
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
|
"checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963"
|
||||||
"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098"
|
"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098"
|
||||||
"checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e"
|
"checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e"
|
||||||
|
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
|
||||||
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc"
|
||||||
"checksum unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aa2700417c405c38f5e6902d699345241c28c0b7ade4abaad71e35a87eb1564"
|
"checksum unix_socket 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aa2700417c405c38f5e6902d699345241c28c0b7ade4abaad71e35a87eb1564"
|
||||||
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
|
"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
|
||||||
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
|
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
|
||||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
|
||||||
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
|
"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3"
|
||||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
31
Cargo.toml
31
Cargo.toml
@ -5,17 +5,17 @@ authors = ["daa84 <daa84@inbox.ru>"]
|
|||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cairo-rs = "0.3"
|
cairo-rs = "0.4"
|
||||||
pango = "0.3"
|
pango = "0.4"
|
||||||
pango-sys = "0.5"
|
pango-sys = "0.6"
|
||||||
pangocairo = "0.4"
|
pangocairo = "0.5"
|
||||||
pangocairo-sys = "0.6"
|
pangocairo-sys = "0.7"
|
||||||
glib = "0.4"
|
glib = "0.5"
|
||||||
glib-sys = "0.5"
|
glib-sys = "0.6"
|
||||||
gdk = "0.7"
|
gdk = "0.8"
|
||||||
gdk-sys = "0.5"
|
gdk-sys = "0.6"
|
||||||
gio = "0.3"
|
gio = "0.4"
|
||||||
gobject-sys = "0.5"
|
gobject-sys = "0.6"
|
||||||
#gdk = { git = 'https://github.com/gtk-rs/gdk' }
|
#gdk = { git = 'https://github.com/gtk-rs/gdk' }
|
||||||
#gdk-sys = { git = 'https://github.com/gtk-rs/sys' }
|
#gdk-sys = { git = 'https://github.com/gtk-rs/sys' }
|
||||||
#glib = { git = 'https://github.com/gtk-rs/glib' }
|
#glib = { git = 'https://github.com/gtk-rs/glib' }
|
||||||
@ -31,6 +31,11 @@ phf = "0.7"
|
|||||||
log = "0.4"
|
log = "0.4"
|
||||||
env_logger = "0.5"
|
env_logger = "0.5"
|
||||||
htmlescape = "0.3"
|
htmlescape = "0.3"
|
||||||
|
rmpv = "0.4"
|
||||||
|
percent-encoding = "1.0"
|
||||||
|
regex = "0.2"
|
||||||
|
lazy_static = "1.0"
|
||||||
|
unicode-width = "0.1.4"
|
||||||
|
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
@ -44,12 +49,12 @@ serde_json = "1.0"
|
|||||||
phf_codegen = "0.7"
|
phf_codegen = "0.7"
|
||||||
|
|
||||||
[dependencies.gtk]
|
[dependencies.gtk]
|
||||||
version = "0.3"
|
version = "0.4"
|
||||||
features = ["v3_22"]
|
features = ["v3_22"]
|
||||||
#git = "https://github.com/gtk-rs/gtk"
|
#git = "https://github.com/gtk-rs/gtk"
|
||||||
|
|
||||||
[dependencies.gtk-sys]
|
[dependencies.gtk-sys]
|
||||||
version = "0.5"
|
version = "0.6"
|
||||||
features = ["v3_22"]
|
features = ["v3_22"]
|
||||||
#git = 'https://github.com/gtk-rs/sys'
|
#git = 'https://github.com/gtk-rs/sys'
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ environment:
|
|||||||
PROJECT_NAME: neovim-gtk-win64
|
PROJECT_NAME: neovim-gtk-win64
|
||||||
matrix:
|
matrix:
|
||||||
- TARGET: x86_64-pc-windows-gnu
|
- TARGET: x86_64-pc-windows-gnu
|
||||||
RUST_VERSION: 1.22.1
|
RUST_VERSION: 1.24.0
|
||||||
|
|
||||||
install:
|
install:
|
||||||
# - ps: Start-FileDownload "https://static.rust-lang.org/dist/channel-rust-stable"
|
# - ps: Start-FileDownload "https://static.rust-lang.org/dist/channel-rust-stable"
|
||||||
|
141
resources/side-panel.ui
Normal file
141
resources/side-panel.ui
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Generated with glade 3.20.4 -->
|
||||||
|
<interface>
|
||||||
|
<requires lib="gtk+" version="3.8"/>
|
||||||
|
<object class="GtkTreeStore" id="dir_list_model">
|
||||||
|
<columns>
|
||||||
|
<!-- column-name dir_name -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name icon_name -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name path -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
</columns>
|
||||||
|
</object>
|
||||||
|
<object class="GtkMenu" id="file_browser_context_menu">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="action_name">filebrowser.cd</property>
|
||||||
|
<property name="label" translatable="yes">Go To Directory</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkSeparatorMenuItem">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkMenuItem">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="action_name">filebrowser.reload</property>
|
||||||
|
<property name="label" translatable="yes">Reload</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckMenuItem" id="file_browser_show_hidden_checkbox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label">Show Hidden Files</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<object class="GtkTreeStore" id="file_browser_tree_store">
|
||||||
|
<columns>
|
||||||
|
<!-- column-name filename -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name path -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
<!-- column-name file_type -->
|
||||||
|
<column type="guchar"/>
|
||||||
|
<!-- column-name icon_name -->
|
||||||
|
<column type="gchararray"/>
|
||||||
|
</columns>
|
||||||
|
</object>
|
||||||
|
<object class="GtkBox" id="file_browser">
|
||||||
|
<property name="width_request">150</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkComboBox" id="dir_list">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="focus_on_click">False</property>
|
||||||
|
<property name="border_width">6</property>
|
||||||
|
<property name="model">dir_list_model</property>
|
||||||
|
<property name="wrap_width">1</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererPixbuf">
|
||||||
|
<property name="xpad">6</property>
|
||||||
|
</object>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="icon-name">1</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererText">
|
||||||
|
<property name="xpad">6</property>
|
||||||
|
<property name="ellipsize">end</property>
|
||||||
|
</object>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="text">0</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScrolledWindow">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeView" id="file_browser_tree_view">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="model">file_browser_tree_store</property>
|
||||||
|
<property name="headers_visible">False</property>
|
||||||
|
<property name="show_expanders">False</property>
|
||||||
|
<property name="level_indentation">20</property>
|
||||||
|
<property name="activate_on_single_click">True</property>
|
||||||
|
<child internal-child="selection">
|
||||||
|
<object class="GtkTreeSelection"/>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkTreeViewColumn">
|
||||||
|
<property name="sizing">autosize</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererPixbuf">
|
||||||
|
<property name="xpad">6</property>
|
||||||
|
</object>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="icon-name">3</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCellRendererText"/>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="text">0</attribute>
|
||||||
|
</attributes>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<style>
|
||||||
|
<class name="view"/>
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</interface>
|
@ -54,3 +54,6 @@ function s:GuiFontCommand(fname, bang) abort
|
|||||||
endfunction
|
endfunction
|
||||||
command! -nargs=? -bang Guifont call s:GuiFontCommand("<args>", "<bang>")
|
command! -nargs=? -bang Guifont call s:GuiFontCommand("<args>", "<bang>")
|
||||||
command! -nargs=? -bang GuiFont call s:GuiFontCommand("<args>", "<bang>")
|
command! -nargs=? -bang GuiFont call s:GuiFontCommand("<args>", "<bang>")
|
||||||
|
|
||||||
|
command! NGToggleSidebar call rpcnotify(1, 'Gui', 'Command', 'ToggleSidebar')
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 91 KiB |
481
src/cmd_line.rs
Normal file
481
src/cmd_line.rs
Normal file
@ -0,0 +1,481 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::cmp::max;
|
||||||
|
|
||||||
|
use gtk;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use cairo;
|
||||||
|
|
||||||
|
use neovim_lib::Value;
|
||||||
|
|
||||||
|
use ui_model::{Attrs, ModelLayout};
|
||||||
|
use ui::UiMutex;
|
||||||
|
use render::{self, CellMetrics};
|
||||||
|
use shell;
|
||||||
|
use cursor;
|
||||||
|
|
||||||
|
pub struct Level {
|
||||||
|
model_layout: ModelLayout,
|
||||||
|
prompt_offset: usize,
|
||||||
|
preferred_width: i32,
|
||||||
|
preferred_height: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Level {
|
||||||
|
pub fn insert(&mut self, c: &str, shift: bool, render_state: &shell::RenderState) {
|
||||||
|
self.model_layout.insert_char(c, shift);
|
||||||
|
self.update_preferred_size(render_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn replace_from_ctx(&mut self, ctx: &CmdLineContext, render_state: &shell::RenderState) {
|
||||||
|
let content = ctx.get_lines();
|
||||||
|
self.replace_line(content.lines, false);
|
||||||
|
self.prompt_offset = content.prompt_offset;
|
||||||
|
self.model_layout
|
||||||
|
.set_cursor(self.prompt_offset + ctx.pos as usize);
|
||||||
|
self.update_preferred_size(render_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_ctx(ctx: &CmdLineContext, render_state: &shell::RenderState) -> Self {
|
||||||
|
let content = ctx.get_lines();
|
||||||
|
let mut level = Level::from_lines(content.lines, ctx.max_width, render_state);
|
||||||
|
|
||||||
|
level.prompt_offset = content.prompt_offset;
|
||||||
|
level
|
||||||
|
.model_layout
|
||||||
|
.set_cursor(level.prompt_offset + ctx.pos as usize);
|
||||||
|
level.update_preferred_size(render_state);
|
||||||
|
|
||||||
|
level
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_line(&mut self, lines: Vec<Vec<(Option<Attrs>, Vec<char>)>>, append: bool) {
|
||||||
|
if append {
|
||||||
|
self.model_layout.layout_append(lines);
|
||||||
|
} else {
|
||||||
|
self.model_layout.layout(lines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_preferred_size(&mut self, render_state: &shell::RenderState) {
|
||||||
|
let &CellMetrics {
|
||||||
|
line_height,
|
||||||
|
char_width,
|
||||||
|
..
|
||||||
|
} = render_state.font_ctx.cell_metrics();
|
||||||
|
|
||||||
|
let (columns, rows) = self.model_layout.size();
|
||||||
|
let columns = max(columns, 5);
|
||||||
|
|
||||||
|
self.preferred_width = (char_width * columns as f64) as i32;
|
||||||
|
self.preferred_height = (line_height * rows as f64) as i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_attributed_content(
|
||||||
|
content: &Vec<Vec<(HashMap<String, Value>, String)>>,
|
||||||
|
) -> Vec<Vec<(Option<Attrs>, Vec<char>)>> {
|
||||||
|
content
|
||||||
|
.iter()
|
||||||
|
.map(|line_chars| {
|
||||||
|
line_chars
|
||||||
|
.iter()
|
||||||
|
.map(|c| (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_multiline_content(
|
||||||
|
content: &Vec<Vec<(HashMap<String, Value>, String)>>,
|
||||||
|
max_width: i32,
|
||||||
|
render_state: &shell::RenderState,
|
||||||
|
) -> Self {
|
||||||
|
Level::from_lines(
|
||||||
|
Level::to_attributed_content(content),
|
||||||
|
max_width,
|
||||||
|
render_state,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_lines(
|
||||||
|
lines: Vec<Vec<(Option<Attrs>, Vec<char>)>>,
|
||||||
|
max_width: i32,
|
||||||
|
render_state: &shell::RenderState,
|
||||||
|
) -> Self {
|
||||||
|
let &CellMetrics { char_width, .. } = render_state.font_ctx.cell_metrics();
|
||||||
|
|
||||||
|
let max_width_chars = (max_width as f64 / char_width) as u64;
|
||||||
|
|
||||||
|
let mut model_layout = ModelLayout::new(max_width_chars);
|
||||||
|
model_layout.layout(lines);
|
||||||
|
|
||||||
|
let mut level = Level {
|
||||||
|
model_layout,
|
||||||
|
preferred_width: -1,
|
||||||
|
preferred_height: -1,
|
||||||
|
prompt_offset: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
level.update_preferred_size(render_state);
|
||||||
|
level
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_cache(&mut self, render_state: &shell::RenderState) {
|
||||||
|
render::shape_dirty(
|
||||||
|
&render_state.font_ctx,
|
||||||
|
&mut self.model_layout.model,
|
||||||
|
&render_state.color_model,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_cursor(&mut self, render_state: &shell::RenderState, pos: usize) {
|
||||||
|
self.model_layout.set_cursor(self.prompt_offset + pos);
|
||||||
|
self.update_preferred_size(render_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prompt_lines(
|
||||||
|
firstc: &str,
|
||||||
|
prompt: &str,
|
||||||
|
indent: u64,
|
||||||
|
) -> (usize, Vec<(Option<Attrs>, Vec<char>)>) {
|
||||||
|
let prompt: Vec<(Option<Attrs>, Vec<char>)> = if !firstc.is_empty() {
|
||||||
|
if firstc.len() >= indent as usize {
|
||||||
|
vec![(None, firstc.chars().collect())]
|
||||||
|
} else {
|
||||||
|
vec![
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
firstc
|
||||||
|
.chars()
|
||||||
|
.chain((firstc.len()..indent as usize).map(|_| ' '))
|
||||||
|
.collect(),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} else if !prompt.is_empty() {
|
||||||
|
prompt
|
||||||
|
.lines()
|
||||||
|
.map(|l| (None, l.chars().collect()))
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
let prompt_offset = prompt.last().map(|l| l.1.len()).unwrap_or(0);
|
||||||
|
|
||||||
|
(prompt_offset, prompt)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
levels: Vec<Level>,
|
||||||
|
block: Option<Level>,
|
||||||
|
render_state: Rc<RefCell<shell::RenderState>>,
|
||||||
|
drawing_area: gtk::DrawingArea,
|
||||||
|
cursor: Option<cursor::BlinkCursor<State>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn new(drawing_area: gtk::DrawingArea, render_state: Rc<RefCell<shell::RenderState>>) -> Self {
|
||||||
|
State {
|
||||||
|
levels: Vec::new(),
|
||||||
|
block: None,
|
||||||
|
render_state,
|
||||||
|
drawing_area,
|
||||||
|
cursor: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_area_size(&self) {
|
||||||
|
let drawing_area = self.drawing_area.clone();
|
||||||
|
let block = self.block.as_ref();
|
||||||
|
let level = self.levels.last();
|
||||||
|
|
||||||
|
let (block_width, block_height) = block
|
||||||
|
.map(|b| (b.preferred_width, b.preferred_height))
|
||||||
|
.unwrap_or((0, 0));
|
||||||
|
let (level_width, level_height) = level
|
||||||
|
.map(|l| (l.preferred_width, l.preferred_height))
|
||||||
|
.unwrap_or((0, 0));
|
||||||
|
|
||||||
|
drawing_area.set_size_request(
|
||||||
|
max(level_width, block_width),
|
||||||
|
max(block_height + level_height, 40),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn preferred_height(&self) -> i32 {
|
||||||
|
let level = self.levels.last();
|
||||||
|
level.map(|l| l.preferred_height).unwrap_or(0)
|
||||||
|
+ self.block.as_ref().map(|b| b.preferred_height).unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_cursor(&mut self, render_state: &shell::RenderState, pos: usize, level: usize) {
|
||||||
|
debug_assert!(level > 0);
|
||||||
|
|
||||||
|
// queue old cursor position
|
||||||
|
self.queue_redraw_cursor();
|
||||||
|
|
||||||
|
self.levels
|
||||||
|
.get_mut(level - 1)
|
||||||
|
.map(|l| l.set_cursor(render_state, pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn queue_redraw_cursor(&mut self) {
|
||||||
|
if let Some(ref level) = self.levels.last() {
|
||||||
|
let level_preferred_height = level.preferred_height;
|
||||||
|
let block_preferred_height =
|
||||||
|
self.block.as_ref().map(|b| b.preferred_height).unwrap_or(0);
|
||||||
|
|
||||||
|
let gap = self.drawing_area.get_allocated_height() - level_preferred_height
|
||||||
|
- block_preferred_height;
|
||||||
|
|
||||||
|
let model = &level.model_layout.model;
|
||||||
|
|
||||||
|
let mut cur_point = model.cur_point();
|
||||||
|
cur_point.extend_by_items(model);
|
||||||
|
|
||||||
|
let render_state = self.render_state.borrow();
|
||||||
|
let cell_metrics = render_state.font_ctx.cell_metrics();
|
||||||
|
|
||||||
|
let (x, y, width, height) = cur_point.to_area_extend_ink(model, cell_metrics);
|
||||||
|
|
||||||
|
if gap > 0 {
|
||||||
|
self.drawing_area
|
||||||
|
.queue_draw_area(x, y + gap / 2, width, height);
|
||||||
|
} else {
|
||||||
|
self.drawing_area
|
||||||
|
.queue_draw_area(x, y + block_preferred_height, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl cursor::CursorRedrawCb for State {
|
||||||
|
fn queue_redraw_cursor(&mut self) {
|
||||||
|
self.queue_redraw_cursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CmdLine {
|
||||||
|
popover: gtk::Popover,
|
||||||
|
displyed: bool,
|
||||||
|
state: Arc<UiMutex<State>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmdLine {
|
||||||
|
pub fn new(drawing: >k::DrawingArea, render_state: Rc<RefCell<shell::RenderState>>) -> Self {
|
||||||
|
let popover = gtk::Popover::new(Some(drawing));
|
||||||
|
popover.set_modal(false);
|
||||||
|
popover.set_position(gtk::PositionType::Right);
|
||||||
|
|
||||||
|
let drawing_area = gtk::DrawingArea::new();
|
||||||
|
drawing_area.show_all();
|
||||||
|
popover.add(&drawing_area);
|
||||||
|
|
||||||
|
let state = Arc::new(UiMutex::new(State::new(drawing_area.clone(), render_state)));
|
||||||
|
let weak_cb = Arc::downgrade(&state);
|
||||||
|
let cursor = cursor::BlinkCursor::new(weak_cb);
|
||||||
|
state.borrow_mut().cursor = Some(cursor);
|
||||||
|
|
||||||
|
drawing_area.connect_draw(clone!(state => move |_, ctx| gtk_draw(ctx, &state)));
|
||||||
|
|
||||||
|
CmdLine {
|
||||||
|
popover,
|
||||||
|
state,
|
||||||
|
displyed: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_level(&mut self, ctx: &CmdLineContext) {
|
||||||
|
let mut state = self.state.borrow_mut();
|
||||||
|
let render_state = state.render_state.clone();
|
||||||
|
let render_state = render_state.borrow();
|
||||||
|
|
||||||
|
if ctx.level_idx as usize == state.levels.len() {
|
||||||
|
let level = state.levels.last_mut().unwrap();
|
||||||
|
level.replace_from_ctx(ctx, &*render_state);
|
||||||
|
level.update_cache(&*render_state);
|
||||||
|
} else {
|
||||||
|
let mut level = Level::from_ctx(ctx, &*render_state);
|
||||||
|
level.update_cache(&*render_state);
|
||||||
|
state.levels.push(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.request_area_size();
|
||||||
|
|
||||||
|
if !self.displyed {
|
||||||
|
self.displyed = true;
|
||||||
|
self.popover.set_pointing_to(>k::Rectangle {
|
||||||
|
x: ctx.x,
|
||||||
|
y: ctx.y,
|
||||||
|
width: ctx.width,
|
||||||
|
height: ctx.height,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.popover.popup();
|
||||||
|
state.cursor.as_mut().unwrap().start();
|
||||||
|
} else {
|
||||||
|
state.drawing_area.queue_draw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn special_char(
|
||||||
|
&self,
|
||||||
|
render_state: &shell::RenderState,
|
||||||
|
c: String,
|
||||||
|
shift: bool,
|
||||||
|
level: u64,
|
||||||
|
) {
|
||||||
|
let mut state = self.state.borrow_mut();
|
||||||
|
|
||||||
|
if let Some(level) = state.levels.get_mut((level - 1) as usize) {
|
||||||
|
level.insert(&c, shift, render_state);
|
||||||
|
level.update_cache(&*render_state);
|
||||||
|
} else {
|
||||||
|
error!("Level {} does not exists", level);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.request_area_size();
|
||||||
|
state.drawing_area.queue_draw()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hide_level(&mut self, level_idx: u64) {
|
||||||
|
let mut state = self.state.borrow_mut();
|
||||||
|
|
||||||
|
if level_idx as usize == state.levels.len() {
|
||||||
|
state.levels.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.levels.is_empty() {
|
||||||
|
self.popover.hide();
|
||||||
|
self.displyed = false;
|
||||||
|
state.cursor.as_mut().unwrap().leave_focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show_block(
|
||||||
|
&mut self,
|
||||||
|
content: &Vec<Vec<(HashMap<String, Value>, String)>>,
|
||||||
|
max_width: i32,
|
||||||
|
) {
|
||||||
|
let mut state = self.state.borrow_mut();
|
||||||
|
let mut block =
|
||||||
|
Level::from_multiline_content(content, max_width, &*state.render_state.borrow());
|
||||||
|
block.update_cache(&*state.render_state.borrow());
|
||||||
|
state.block = Some(block);
|
||||||
|
state.request_area_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_append(&mut self, content: &Vec<(HashMap<String, Value>, String)>) {
|
||||||
|
let mut state = self.state.borrow_mut();
|
||||||
|
let render_state = state.render_state.clone();
|
||||||
|
{
|
||||||
|
let attr_content = content
|
||||||
|
.iter()
|
||||||
|
.map(|c| (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let block = state.block.as_mut().unwrap();
|
||||||
|
block.replace_line(vec![attr_content], true);
|
||||||
|
block.update_preferred_size(&*render_state.borrow());
|
||||||
|
block.update_cache(&*render_state.borrow());
|
||||||
|
}
|
||||||
|
state.request_area_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_hide(&mut self) {
|
||||||
|
self.state.borrow_mut().block = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pos(&mut self, render_state: &shell::RenderState, pos: u64, level: u64) {
|
||||||
|
self.state
|
||||||
|
.borrow_mut()
|
||||||
|
.set_cursor(render_state, pos as usize, level as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gtk_draw(ctx: &cairo::Context, state: &Arc<UiMutex<State>>) -> Inhibit {
|
||||||
|
let state = state.borrow();
|
||||||
|
let preferred_height = state.preferred_height();
|
||||||
|
let level = state.levels.last();
|
||||||
|
let block = state.block.as_ref();
|
||||||
|
|
||||||
|
let render_state = state.render_state.borrow();
|
||||||
|
|
||||||
|
let gap = state.drawing_area.get_allocated_height() - preferred_height;
|
||||||
|
if gap > 0 {
|
||||||
|
ctx.translate(0.0, (gap / 2) as f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
render::clear(ctx, &render_state.color_model);
|
||||||
|
|
||||||
|
if let Some(block) = block {
|
||||||
|
render::render(
|
||||||
|
ctx,
|
||||||
|
&cursor::EmptyCursor::new(),
|
||||||
|
&render_state.font_ctx,
|
||||||
|
&block.model_layout.model,
|
||||||
|
&render_state.color_model,
|
||||||
|
&render_state.mode,
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.translate(0.0, block.preferred_height as f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(level) = level {
|
||||||
|
render::render(
|
||||||
|
ctx,
|
||||||
|
state.cursor.as_ref().unwrap(),
|
||||||
|
&render_state.font_ctx,
|
||||||
|
&level.model_layout.model,
|
||||||
|
&render_state.color_model,
|
||||||
|
&render_state.mode,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Inhibit(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CmdLineContext {
|
||||||
|
pub content: Vec<(HashMap<String, Value>, String)>,
|
||||||
|
pub pos: u64,
|
||||||
|
pub firstc: String,
|
||||||
|
pub prompt: String,
|
||||||
|
pub indent: u64,
|
||||||
|
pub level_idx: u64,
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
pub width: i32,
|
||||||
|
pub height: i32,
|
||||||
|
pub max_width: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmdLineContext {
|
||||||
|
fn get_lines(&self) -> LineContent {
|
||||||
|
let content_line: Vec<(Option<Attrs>, Vec<char>)> = self.content
|
||||||
|
.iter()
|
||||||
|
.map(|c| (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()))
|
||||||
|
.collect();
|
||||||
|
let (prompt_offset, prompt_lines) = prompt_lines(&self.firstc, &self.prompt, self.indent);
|
||||||
|
|
||||||
|
let mut content: Vec<_> = prompt_lines.into_iter().map(|line| vec![line]).collect();
|
||||||
|
|
||||||
|
if content.is_empty() {
|
||||||
|
content.push(content_line);
|
||||||
|
} else {
|
||||||
|
content.last_mut().map(|line| line.extend(content_line));
|
||||||
|
}
|
||||||
|
|
||||||
|
LineContent {
|
||||||
|
lines: content,
|
||||||
|
prompt_offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LineContent {
|
||||||
|
lines: Vec<Vec<(Option<Attrs>, Vec<char>)>>,
|
||||||
|
prompt_offset: usize,
|
||||||
|
}
|
113
src/cursor.rs
113
src/cursor.rs
@ -1,10 +1,8 @@
|
|||||||
use cairo;
|
use cairo;
|
||||||
use color::Color;
|
use color::Color;
|
||||||
use ui::UiMutex;
|
use ui::UiMutex;
|
||||||
use shell;
|
|
||||||
use mode;
|
use mode;
|
||||||
use nvim;
|
use nvim;
|
||||||
use nvim::{RepaintMode, RedrawEvents};
|
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
use render;
|
use render;
|
||||||
use render::CellMetrics;
|
use render::CellMetrics;
|
||||||
@ -44,20 +42,20 @@ enum AnimPhase {
|
|||||||
Busy,
|
Busy,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct State {
|
struct State<CB: CursorRedrawCb> {
|
||||||
alpha: Alpha,
|
alpha: Alpha,
|
||||||
anim_phase: AnimPhase,
|
anim_phase: AnimPhase,
|
||||||
shell: Weak<UiMutex<shell::State>>,
|
redraw_cb: Weak<UiMutex<CB>>,
|
||||||
|
|
||||||
timer: Option<glib::SourceId>,
|
timer: Option<glib::SourceId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl<CB: CursorRedrawCb> State<CB> {
|
||||||
fn new(shell: Weak<UiMutex<shell::State>>) -> State {
|
fn new(redraw_cb: Weak<UiMutex<CB>>) -> Self {
|
||||||
State {
|
State {
|
||||||
alpha: Alpha(1.0),
|
alpha: Alpha(1.0),
|
||||||
anim_phase: AnimPhase::Shown,
|
anim_phase: AnimPhase::Shown,
|
||||||
shell: shell,
|
redraw_cb,
|
||||||
timer: None,
|
timer: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -71,13 +69,48 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Cursor {
|
pub trait Cursor {
|
||||||
state: Arc<UiMutex<State>>,
|
fn draw(
|
||||||
|
&self,
|
||||||
|
ctx: &cairo::Context,
|
||||||
|
font_ctx: &render::Context,
|
||||||
|
mode: &mode::Mode,
|
||||||
|
line_y: f64,
|
||||||
|
double_width: bool,
|
||||||
|
bg: &Color,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cursor {
|
pub struct EmptyCursor;
|
||||||
pub fn new(shell: Weak<UiMutex<shell::State>>) -> Cursor {
|
|
||||||
Cursor { state: Arc::new(UiMutex::new(State::new(shell))) }
|
impl EmptyCursor {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
EmptyCursor { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cursor for EmptyCursor {
|
||||||
|
fn draw(
|
||||||
|
&self,
|
||||||
|
_ctx: &cairo::Context,
|
||||||
|
_font_ctx: &render::Context,
|
||||||
|
_mode: &mode::Mode,
|
||||||
|
_line_y: f64,
|
||||||
|
_double_width: bool,
|
||||||
|
_bg: &Color,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BlinkCursor<CB: CursorRedrawCb> {
|
||||||
|
state: Arc<UiMutex<State<CB>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CB: CursorRedrawCb + 'static> BlinkCursor<CB> {
|
||||||
|
pub fn new(redraw_cb: Weak<UiMutex<CB>>) -> Self {
|
||||||
|
BlinkCursor {
|
||||||
|
state: Arc::new(UiMutex::new(State::new(redraw_cb))),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(&mut self) {
|
pub fn start(&mut self) {
|
||||||
@ -112,8 +145,10 @@ impl Cursor {
|
|||||||
pub fn busy_off(&mut self) {
|
pub fn busy_off(&mut self) {
|
||||||
self.start();
|
self.start();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn draw(
|
impl<CB: CursorRedrawCb> Cursor for BlinkCursor<CB> {
|
||||||
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
ctx: &cairo::Context,
|
ctx: &cairo::Context,
|
||||||
font_ctx: &render::Context,
|
font_ctx: &render::Context,
|
||||||
@ -122,7 +157,6 @@ impl Cursor {
|
|||||||
double_width: bool,
|
double_width: bool,
|
||||||
bg: &Color,
|
bg: &Color,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
let state = self.state.borrow();
|
let state = self.state.borrow();
|
||||||
|
|
||||||
if state.anim_phase == AnimPhase::Busy {
|
if state.anim_phase == AnimPhase::Busy {
|
||||||
@ -157,9 +191,7 @@ fn cursor_rect(
|
|||||||
|
|
||||||
if let Some(mode_info) = mode.mode_info() {
|
if let Some(mode_info) = mode.mode_info() {
|
||||||
match mode_info.cursor_shape() {
|
match mode_info.cursor_shape() {
|
||||||
None |
|
None | Some(&nvim::CursorShape::Unknown) | Some(&nvim::CursorShape::Block) => {
|
||||||
Some(&nvim::CursorShape::Unknown) |
|
|
||||||
Some(&nvim::CursorShape::Block) => {
|
|
||||||
let cursor_width = if double_width {
|
let cursor_width = if double_width {
|
||||||
char_width * 2.0
|
char_width * 2.0
|
||||||
} else {
|
} else {
|
||||||
@ -204,7 +236,8 @@ fn cursor_rect(
|
|||||||
(line_y, cursor_width, line_height)
|
(line_y, cursor_width, line_height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn anim_step(state: &Arc<UiMutex<State>>) -> glib::Continue {
|
|
||||||
|
fn anim_step<CB: CursorRedrawCb + 'static>(state: &Arc<UiMutex<State<CB>>>) -> glib::Continue {
|
||||||
let mut mut_state = state.borrow_mut();
|
let mut mut_state = state.borrow_mut();
|
||||||
|
|
||||||
let next_event = match mut_state.anim_phase {
|
let next_event = match mut_state.anim_phase {
|
||||||
@ -212,37 +245,32 @@ fn anim_step(state: &Arc<UiMutex<State>>) -> glib::Continue {
|
|||||||
mut_state.anim_phase = AnimPhase::Hide;
|
mut_state.anim_phase = AnimPhase::Hide;
|
||||||
Some(60)
|
Some(60)
|
||||||
}
|
}
|
||||||
AnimPhase::Hide => {
|
AnimPhase::Hide => if !mut_state.alpha.hide(0.3) {
|
||||||
if !mut_state.alpha.hide(0.3) {
|
mut_state.anim_phase = AnimPhase::Hidden;
|
||||||
mut_state.anim_phase = AnimPhase::Hidden;
|
|
||||||
|
|
||||||
Some(300)
|
Some(300)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
},
|
||||||
}
|
|
||||||
AnimPhase::Hidden => {
|
AnimPhase::Hidden => {
|
||||||
mut_state.anim_phase = AnimPhase::Show;
|
mut_state.anim_phase = AnimPhase::Show;
|
||||||
|
|
||||||
Some(60)
|
Some(60)
|
||||||
}
|
}
|
||||||
AnimPhase::Show => {
|
AnimPhase::Show => if !mut_state.alpha.show(0.3) {
|
||||||
if !mut_state.alpha.show(0.3) {
|
mut_state.anim_phase = AnimPhase::Shown;
|
||||||
mut_state.anim_phase = AnimPhase::Shown;
|
|
||||||
|
|
||||||
Some(500)
|
Some(500)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
},
|
||||||
}
|
AnimPhase::NoFocus => None,
|
||||||
AnimPhase::NoFocus => None,
|
|
||||||
AnimPhase::Busy => None,
|
AnimPhase::Busy => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let shell = mut_state.shell.upgrade().unwrap();
|
let redraw_cb = mut_state.redraw_cb.upgrade().unwrap();
|
||||||
let mut shell = shell.borrow_mut();
|
let mut redraw_cb = redraw_cb.borrow_mut();
|
||||||
let point = shell.model.cur_point();
|
redraw_cb.queue_redraw_cursor();
|
||||||
shell.on_redraw(&RepaintMode::Area(point));
|
|
||||||
|
|
||||||
|
|
||||||
if let Some(timeout) = next_event {
|
if let Some(timeout) = next_event {
|
||||||
@ -253,10 +281,9 @@ fn anim_step(state: &Arc<UiMutex<State>>) -> glib::Continue {
|
|||||||
} else {
|
} else {
|
||||||
glib::Continue(true)
|
glib::Continue(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Cursor {
|
impl<CB: CursorRedrawCb> Drop for BlinkCursor<CB> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(timer_id) = self.state.borrow_mut().timer.take() {
|
if let Some(timer_id) = self.state.borrow_mut().timer.take() {
|
||||||
glib::source_remove(timer_id);
|
glib::source_remove(timer_id);
|
||||||
@ -264,6 +291,10 @@ impl Drop for Cursor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait CursorRedrawCb {
|
||||||
|
fn queue_redraw_cursor(&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
557
src/file_browser.rs
Normal file
557
src/file_browser.rs
Normal file
@ -0,0 +1,557 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::io;
|
||||||
|
use std::fs;
|
||||||
|
use std::fs::DirEntry;
|
||||||
|
use std::path::{Component, Path, PathBuf};
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
use gio;
|
||||||
|
use gio::prelude::*;
|
||||||
|
use gtk;
|
||||||
|
use gtk::MenuExt;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
|
||||||
|
use neovim_lib::{NeovimApi, NeovimApiAsync};
|
||||||
|
|
||||||
|
use misc::escape_filename;
|
||||||
|
use nvim::{ErrorReport, NeovimClient, NeovimRef};
|
||||||
|
use shell;
|
||||||
|
|
||||||
|
const ICON_FOLDER_CLOSED: &str = "folder-symbolic";
|
||||||
|
const ICON_FOLDER_OPEN: &str = "folder-open-symbolic";
|
||||||
|
const ICON_FILE: &str = "text-x-generic-symbolic";
|
||||||
|
|
||||||
|
struct Components {
|
||||||
|
dir_list_model: gtk::TreeStore,
|
||||||
|
dir_list: gtk::ComboBox,
|
||||||
|
context_menu: gtk::Menu,
|
||||||
|
show_hidden_checkbox: gtk::CheckMenuItem,
|
||||||
|
cd_action: gio::SimpleAction,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
current_dir: String,
|
||||||
|
show_hidden: bool,
|
||||||
|
selected_path: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FileBrowserWidget {
|
||||||
|
store: gtk::TreeStore,
|
||||||
|
tree: gtk::TreeView,
|
||||||
|
widget: gtk::Box,
|
||||||
|
nvim: Option<Rc<NeovimClient>>,
|
||||||
|
comps: Components,
|
||||||
|
state: Rc<RefCell<State>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for FileBrowserWidget {
|
||||||
|
type Target = gtk::Box;
|
||||||
|
|
||||||
|
fn deref(&self) -> >k::Box {
|
||||||
|
&self.widget
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
enum FileType {
|
||||||
|
File,
|
||||||
|
Dir,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
enum Column {
|
||||||
|
Filename,
|
||||||
|
Path,
|
||||||
|
FileType,
|
||||||
|
IconName,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileBrowserWidget {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let builder = gtk::Builder::new_from_string(include_str!("../resources/side-panel.ui"));
|
||||||
|
let widget: gtk::Box = builder.get_object("file_browser").unwrap();
|
||||||
|
let tree: gtk::TreeView = builder.get_object("file_browser_tree_view").unwrap();
|
||||||
|
let store: gtk::TreeStore = builder.get_object("file_browser_tree_store").unwrap();
|
||||||
|
let dir_list_model: gtk::TreeStore = builder.get_object("dir_list_model").unwrap();
|
||||||
|
let dir_list: gtk::ComboBox = builder.get_object("dir_list").unwrap();
|
||||||
|
let context_menu: gtk::Menu = builder.get_object("file_browser_context_menu").unwrap();
|
||||||
|
let show_hidden_checkbox: gtk::CheckMenuItem = builder
|
||||||
|
.get_object("file_browser_show_hidden_checkbox")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let file_browser = FileBrowserWidget {
|
||||||
|
store,
|
||||||
|
tree,
|
||||||
|
widget,
|
||||||
|
nvim: None,
|
||||||
|
comps: Components {
|
||||||
|
dir_list_model,
|
||||||
|
dir_list,
|
||||||
|
context_menu,
|
||||||
|
show_hidden_checkbox,
|
||||||
|
cd_action: gio::SimpleAction::new("cd", None),
|
||||||
|
},
|
||||||
|
state: Rc::new(RefCell::new(State {
|
||||||
|
current_dir: "".to_owned(),
|
||||||
|
show_hidden: false,
|
||||||
|
selected_path: None,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
file_browser
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nvim(&self) -> Option<NeovimRef> {
|
||||||
|
self.nvim.as_ref().unwrap().nvim()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(&mut self, shell_state: &shell::State) {
|
||||||
|
// Initialize values.
|
||||||
|
let nvim = shell_state.nvim_clone();
|
||||||
|
self.nvim = Some(nvim);
|
||||||
|
if let Some(dir) = get_current_dir(&mut self.nvim().unwrap()) {
|
||||||
|
update_dir_list(&dir, &self.comps.dir_list_model, &self.comps.dir_list);
|
||||||
|
self.state.borrow_mut().current_dir = dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate tree.
|
||||||
|
tree_reload(&self.store, &self.state.borrow());
|
||||||
|
|
||||||
|
let store = &self.store;
|
||||||
|
let state_ref = &self.state;
|
||||||
|
self.tree.connect_test_expand_row(clone!(store, state_ref => move |_, iter, _| {
|
||||||
|
store.set(&iter, &[Column::IconName as u32], &[&ICON_FOLDER_OPEN]);
|
||||||
|
// We cannot recursively populate all directories. Instead, we have prepared a single
|
||||||
|
// empty child entry for all non-empty directories, so the row will be expandable. Now,
|
||||||
|
// when a directory is expanded, populate its children.
|
||||||
|
let state = state_ref.borrow();
|
||||||
|
if let Some(child) = store.iter_children(iter) {
|
||||||
|
let filename = store.get_value(&child, Column::Filename as i32);
|
||||||
|
if filename.get::<&str>().is_none() {
|
||||||
|
store.remove(&child);
|
||||||
|
let dir_value = store.get_value(&iter, Column::Path as i32);
|
||||||
|
if let Some(dir) = dir_value.get() {
|
||||||
|
populate_tree_nodes(&store, &state, dir, Some(iter));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// This directory is already populated, i.e. it has been expanded and collapsed
|
||||||
|
// again. Rows further down the tree might have been silently collapsed without
|
||||||
|
// getting an event. Update their folder icon.
|
||||||
|
let mut tree_path = store.get_path(&child).unwrap();
|
||||||
|
while let Some(iter) = store.get_iter(&tree_path) {
|
||||||
|
tree_path.next();
|
||||||
|
let file_type = store
|
||||||
|
.get_value(&iter, Column::FileType as i32)
|
||||||
|
.get::<u8>();
|
||||||
|
if file_type == Some(FileType::Dir as u8) {
|
||||||
|
store.set(&iter, &[Column::IconName as u32], &[&ICON_FOLDER_CLOSED]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Inhibit(false)
|
||||||
|
}));
|
||||||
|
|
||||||
|
self.tree.connect_row_collapsed(clone!(store => move |_, iter, _| {
|
||||||
|
store.set(&iter, &[Column::IconName as u32], &[&ICON_FOLDER_CLOSED]);
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Further initialization.
|
||||||
|
self.init_actions();
|
||||||
|
self.init_subscriptions(shell_state);
|
||||||
|
self.connect_events();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_actions(&self) {
|
||||||
|
let actions = gio::SimpleActionGroup::new();
|
||||||
|
|
||||||
|
let store = &self.store;
|
||||||
|
let state_ref = &self.state;
|
||||||
|
let nvim_ref = self.nvim.as_ref().unwrap();
|
||||||
|
|
||||||
|
let reload_action = gio::SimpleAction::new("reload", None);
|
||||||
|
reload_action.connect_activate(clone!(store, state_ref => move |_, _| {
|
||||||
|
tree_reload(&store, &state_ref.borrow());
|
||||||
|
}));
|
||||||
|
actions.add_action(&reload_action);
|
||||||
|
|
||||||
|
let cd_action = &self.comps.cd_action;
|
||||||
|
cd_action.connect_activate(clone!(state_ref, nvim_ref => move |_, _| {
|
||||||
|
let mut nvim = nvim_ref.nvim().unwrap();
|
||||||
|
if let Some(ref path) = state_ref.borrow().selected_path {
|
||||||
|
nvim.set_current_dir_async(&path)
|
||||||
|
.cb(|r| r.report_err())
|
||||||
|
.call();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
actions.add_action(cd_action);
|
||||||
|
|
||||||
|
self.comps
|
||||||
|
.context_menu
|
||||||
|
.insert_action_group("filebrowser", &actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_subscriptions(&self, shell_state: &shell::State) {
|
||||||
|
// Always set the current working directory as the root of the file browser.
|
||||||
|
let store = &self.store;
|
||||||
|
let state_ref = &self.state;
|
||||||
|
let dir_list_model = &self.comps.dir_list_model;
|
||||||
|
let dir_list = &self.comps.dir_list;
|
||||||
|
shell_state.subscribe(
|
||||||
|
"DirChanged",
|
||||||
|
&["getcwd()"],
|
||||||
|
clone!(store, state_ref, dir_list_model, dir_list => move |args| {
|
||||||
|
let dir = args.into_iter().next().unwrap();
|
||||||
|
if dir != state_ref.borrow().current_dir {
|
||||||
|
state_ref.borrow_mut().current_dir = dir.to_owned();
|
||||||
|
update_dir_list(&dir, &dir_list_model, &dir_list);
|
||||||
|
tree_reload(&store, &state_ref.borrow());
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reveal the file of an entered buffer in the file browser and select the entry.
|
||||||
|
let tree = &self.tree;
|
||||||
|
let subscription = shell_state.subscribe(
|
||||||
|
"BufEnter",
|
||||||
|
&["getcwd()", "expand('%:p')"],
|
||||||
|
clone!(tree, store => move |args| {
|
||||||
|
let mut args_iter = args.into_iter();
|
||||||
|
let dir = args_iter.next().unwrap();
|
||||||
|
let file_path = args_iter.next().unwrap();
|
||||||
|
let could_reveal =
|
||||||
|
if let Ok(rel_path) = Path::new(&file_path).strip_prefix(&Path::new(&dir)) {
|
||||||
|
reveal_path_in_tree(&store, &tree, &rel_path)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
if !could_reveal {
|
||||||
|
tree.get_selection().unselect_all();
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
shell_state.run_now(&subscription);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn connect_events(&self) {
|
||||||
|
// Open file / go to dir, when user clicks on an entry.
|
||||||
|
let store = &self.store;
|
||||||
|
let state_ref = &self.state;
|
||||||
|
let nvim_ref = self.nvim.as_ref().unwrap();
|
||||||
|
self.tree.connect_row_activated(clone!(store, state_ref, nvim_ref => move |tree, path, _| {
|
||||||
|
let iter = store.get_iter(path).unwrap();
|
||||||
|
let file_type = store
|
||||||
|
.get_value(&iter, Column::FileType as i32)
|
||||||
|
.get::<u8>()
|
||||||
|
.unwrap();
|
||||||
|
let file_path = store
|
||||||
|
.get_value(&iter, Column::Path as i32)
|
||||||
|
.get::<String>()
|
||||||
|
.unwrap();
|
||||||
|
if file_type == FileType::Dir as u8 {
|
||||||
|
let expanded = tree.row_expanded(path);
|
||||||
|
if expanded {
|
||||||
|
tree.collapse_row(path);
|
||||||
|
} else {
|
||||||
|
tree.expand_row(path, false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// FileType::File
|
||||||
|
let cwd = &state_ref.borrow().current_dir;
|
||||||
|
let cwd = Path::new(cwd);
|
||||||
|
let file_path = if let Some(rel_path) = Path::new(&file_path)
|
||||||
|
.strip_prefix(&cwd)
|
||||||
|
.ok()
|
||||||
|
.and_then(|p| p.to_str())
|
||||||
|
{
|
||||||
|
rel_path
|
||||||
|
} else {
|
||||||
|
&file_path
|
||||||
|
};
|
||||||
|
let file_path = escape_filename(file_path);
|
||||||
|
nvim_ref.nvim().unwrap().command_async(&format!(":e {}", file_path))
|
||||||
|
.cb(|r| r.report_err())
|
||||||
|
.call();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Connect directory list.
|
||||||
|
let nvim_ref = self.nvim.as_ref().unwrap();
|
||||||
|
self.comps.dir_list.connect_changed(clone!(nvim_ref, state_ref => move |dir_list| {
|
||||||
|
if let Some(iter) = dir_list.get_active_iter() {
|
||||||
|
let model = dir_list.get_model().unwrap();
|
||||||
|
if let Some(dir) = model.get_value(&iter, 2).get::<&str>() {
|
||||||
|
if dir != state_ref.borrow().current_dir {
|
||||||
|
let mut nvim = nvim_ref.nvim().unwrap();
|
||||||
|
nvim.set_current_dir_async(dir)
|
||||||
|
.cb(|r| r.report_err())
|
||||||
|
.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
let context_menu = &self.comps.context_menu;
|
||||||
|
let cd_action = &self.comps.cd_action;
|
||||||
|
self.tree.connect_button_press_event(
|
||||||
|
clone!(store, state_ref, context_menu, cd_action => move |tree, ev_btn| {
|
||||||
|
// Open context menu on right click.
|
||||||
|
if ev_btn.get_button() == 3 {
|
||||||
|
context_menu.popup_at_pointer(&**ev_btn);
|
||||||
|
let (pos_x, pos_y) = ev_btn.get_position();
|
||||||
|
let iter = tree
|
||||||
|
.get_path_at_pos(pos_x as i32, pos_y as i32)
|
||||||
|
.and_then(|(path, _, _, _)| path)
|
||||||
|
.and_then(|path| store.get_iter(&path));
|
||||||
|
let file_type = iter
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|iter| {
|
||||||
|
store
|
||||||
|
.get_value(&iter, Column::FileType as i32)
|
||||||
|
.get::<u8>()
|
||||||
|
});
|
||||||
|
// Enable the "Go To Directory" action only if the user clicked on a folder.
|
||||||
|
cd_action.set_enabled(file_type == Some(FileType::Dir as u8));
|
||||||
|
let path = iter
|
||||||
|
.and_then(|iter| {
|
||||||
|
store
|
||||||
|
.get_value(&iter, Column::Path as i32)
|
||||||
|
.get::<String>()
|
||||||
|
});
|
||||||
|
state_ref.borrow_mut().selected_path = path;
|
||||||
|
}
|
||||||
|
Inhibit(false)
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Show / hide hidden files when corresponding menu item is toggled.
|
||||||
|
self.comps.show_hidden_checkbox.connect_toggled(clone!(state_ref, store => move |ev| {
|
||||||
|
let mut state = state_ref.borrow_mut();
|
||||||
|
state.show_hidden = ev.get_active();
|
||||||
|
tree_reload(&store, &state);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare function for dir entries.
|
||||||
|
///
|
||||||
|
/// Sorts directories above files.
|
||||||
|
fn cmp_dirs_first(lhs: &DirEntry, rhs: &DirEntry) -> io::Result<Ordering> {
|
||||||
|
let lhs_metadata = fs::metadata(lhs.path())?;
|
||||||
|
let rhs_metadata = fs::metadata(rhs.path())?;
|
||||||
|
if lhs_metadata.is_dir() == rhs_metadata.is_dir() {
|
||||||
|
Ok(lhs.path()
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_lowercase()
|
||||||
|
.cmp(&rhs.path().to_string_lossy().to_lowercase()))
|
||||||
|
} else {
|
||||||
|
if lhs_metadata.is_dir() {
|
||||||
|
Ok(Ordering::Less)
|
||||||
|
} else {
|
||||||
|
Ok(Ordering::Greater)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears an repopulate the entire tree.
|
||||||
|
fn tree_reload(store: >k::TreeStore, state: &State) {
|
||||||
|
let dir = &state.current_dir;
|
||||||
|
store.clear();
|
||||||
|
populate_tree_nodes(store, state, dir, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the dirctory list on top of the file browser.
|
||||||
|
///
|
||||||
|
/// The list represents the path the the current working directory. If the new cwd is a parent of
|
||||||
|
/// the old one, the list is kept and only the active entry is updated. Otherwise, the list is
|
||||||
|
/// replaced with the new path and the last entry is marked active.
|
||||||
|
fn update_dir_list(dir: &str, dir_list_model: >k::TreeStore, dir_list: >k::ComboBox) {
|
||||||
|
// The current working directory path.
|
||||||
|
let complete_path = Path::new(dir);
|
||||||
|
let mut path = PathBuf::new();
|
||||||
|
let mut components = complete_path.components();
|
||||||
|
let mut next = components.next();
|
||||||
|
|
||||||
|
// Iterator over existing dir_list model.
|
||||||
|
let mut dir_list_iter = dir_list_model.get_iter_first();
|
||||||
|
|
||||||
|
// Whether existing entries up to the current position of dir_list_iter are a prefix of the
|
||||||
|
// new current working directory path.
|
||||||
|
let mut is_prefix = true;
|
||||||
|
|
||||||
|
// Iterate over components of the cwd. Simultaneously move dir_list_iter forward.
|
||||||
|
while let Some(dir) = next {
|
||||||
|
next = components.next();
|
||||||
|
let dir_name = &*dir.as_os_str().to_string_lossy();
|
||||||
|
// Assemble path up to current component.
|
||||||
|
path.push(Path::new(&dir));
|
||||||
|
let path_str = path.to_str().unwrap_or_else(|| {
|
||||||
|
error!(
|
||||||
|
"Could not convert path to string: {}\n
|
||||||
|
Directory chooser will not work for that entry.",
|
||||||
|
path.to_string_lossy()
|
||||||
|
);
|
||||||
|
""
|
||||||
|
});
|
||||||
|
// Use the current entry of dir_list, if any, otherwise append a new one.
|
||||||
|
let current_iter = dir_list_iter.unwrap_or_else(|| dir_list_model.append(None));
|
||||||
|
// Check if the current entry is still part of the new cwd.
|
||||||
|
if is_prefix && dir_list_model.get_value(¤t_iter, 0).get::<&str>() != Some(&dir_name)
|
||||||
|
{
|
||||||
|
is_prefix = false;
|
||||||
|
}
|
||||||
|
if next.is_some() {
|
||||||
|
// Update dir_list entry.
|
||||||
|
dir_list_model.set(
|
||||||
|
¤t_iter,
|
||||||
|
&[0, 1, 2],
|
||||||
|
&[&dir_name, &ICON_FOLDER_CLOSED, &path_str],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// We reached the last component of the new cwd path. Set the active entry of dir_list
|
||||||
|
// to this one.
|
||||||
|
dir_list_model.set(
|
||||||
|
¤t_iter,
|
||||||
|
&[0, 1, 2],
|
||||||
|
&[&dir_name, &ICON_FOLDER_OPEN, &path_str],
|
||||||
|
);
|
||||||
|
dir_list.set_active_iter(¤t_iter);
|
||||||
|
};
|
||||||
|
// Advance dir_list_iter.
|
||||||
|
dir_list_iter = if dir_list_model.iter_next(¤t_iter) {
|
||||||
|
Some(current_iter)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We updated the dir list to the point of the current working directory.
|
||||||
|
if let Some(iter) = dir_list_iter {
|
||||||
|
if is_prefix {
|
||||||
|
// If we didn't change any entries to this point and the list contains further entries,
|
||||||
|
// the remaining ones are subdirectories of the cwd and we keep them.
|
||||||
|
loop {
|
||||||
|
dir_list_model.set(&iter, &[1], &[&ICON_FOLDER_CLOSED]);
|
||||||
|
if !dir_list_model.iter_next(&iter) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If we needed to change entries, the following ones are not directories under the
|
||||||
|
// cwd and we clear them.
|
||||||
|
while dir_list_model.remove(&iter) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Populates one level, i.e. one directory of the file browser tree.
|
||||||
|
fn populate_tree_nodes(
|
||||||
|
store: >k::TreeStore,
|
||||||
|
state: &State,
|
||||||
|
dir: &str,
|
||||||
|
parent: Option<>k::TreeIter>,
|
||||||
|
) {
|
||||||
|
let path = Path::new(dir);
|
||||||
|
let read_dir = match path.read_dir() {
|
||||||
|
Ok(read_dir) => read_dir,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Couldn't populate tree: {}", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let iter = read_dir.filter_map(Result::ok);
|
||||||
|
let mut entries: Vec<DirEntry> = if state.show_hidden {
|
||||||
|
iter.collect()
|
||||||
|
} else {
|
||||||
|
iter.filter(|entry| !entry.file_name().to_string_lossy().starts_with("."))
|
||||||
|
.filter(|entry| !entry.file_name().to_string_lossy().ends_with("~"))
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
entries.sort_unstable_by(|lhs, rhs| cmp_dirs_first(lhs, rhs).unwrap_or(Ordering::Equal));
|
||||||
|
for entry in entries {
|
||||||
|
let path = if let Some(path) = entry.path().to_str() {
|
||||||
|
path.to_owned()
|
||||||
|
} else {
|
||||||
|
// Skip paths that contain invalid unicode.
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let filename = entry.file_name().to_str().unwrap().to_owned();
|
||||||
|
let file_type = if let Ok(metadata) = fs::metadata(entry.path()) {
|
||||||
|
let file_type = metadata.file_type();
|
||||||
|
if file_type.is_dir() {
|
||||||
|
FileType::Dir
|
||||||
|
} else if file_type.is_file() {
|
||||||
|
FileType::File
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// In case of invalid symlinks, we cannot obtain metadata.
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let icon = match file_type {
|
||||||
|
FileType::Dir => ICON_FOLDER_CLOSED,
|
||||||
|
FileType::File => ICON_FILE,
|
||||||
|
};
|
||||||
|
// When we get until here, we want to show the entry. Append it to the tree.
|
||||||
|
let iter = store.append(parent);
|
||||||
|
store.set(
|
||||||
|
&iter,
|
||||||
|
&[0, 1, 2, 3],
|
||||||
|
&[&filename, &path, &(file_type as u8), &icon],
|
||||||
|
);
|
||||||
|
// For directories, check whether the directory is empty. If not, append a single empty
|
||||||
|
// entry, so the expand arrow is shown. Its contents are dynamically populated when
|
||||||
|
// expanded (see `init`).
|
||||||
|
if let FileType::Dir = file_type {
|
||||||
|
let not_empty = if let Ok(mut dir) = entry.path().read_dir() {
|
||||||
|
dir.next().is_some()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
if not_empty {
|
||||||
|
let iter = store.append(&iter);
|
||||||
|
store.set(&iter, &[], &[]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_current_dir(nvim: &mut NeovimRef) -> Option<String> {
|
||||||
|
match nvim.eval("getcwd()") {
|
||||||
|
Ok(cwd) => cwd.as_str().map(|s| s.to_owned()),
|
||||||
|
Err(err) => {
|
||||||
|
error!("Couldn't get cwd: {}", err);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reveals and selects the given file in the file browser.
|
||||||
|
///
|
||||||
|
/// Returns `true` if the file could be successfully revealed.
|
||||||
|
fn reveal_path_in_tree(store: >k::TreeStore, tree: >k::TreeView, rel_file_path: &Path) -> bool {
|
||||||
|
let mut tree_path = gtk::TreePath::new();
|
||||||
|
'components: for component in rel_file_path.components() {
|
||||||
|
if let Component::Normal(component) = component {
|
||||||
|
tree_path.down();
|
||||||
|
while let Some(iter) = store.get_iter(&tree_path) {
|
||||||
|
let entry_value = store.get_value(&iter, Column::Filename as i32);
|
||||||
|
let entry = entry_value.get::<&str>().unwrap();
|
||||||
|
if component == entry {
|
||||||
|
tree.expand_row(&tree_path, false);
|
||||||
|
continue 'components;
|
||||||
|
}
|
||||||
|
tree_path.next();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if tree_path.get_depth() == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tree.set_cursor(&tree_path, None, false);
|
||||||
|
true
|
||||||
|
}
|
@ -5,8 +5,6 @@ use gdk::EventKey;
|
|||||||
use phf;
|
use phf;
|
||||||
use neovim_lib::{Neovim, NeovimApi};
|
use neovim_lib::{Neovim, NeovimApi};
|
||||||
|
|
||||||
use std::ascii::AsciiExt;
|
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/key_map_table.rs"));
|
include!(concat!(env!("OUT_DIR"), "/key_map_table.rs"));
|
||||||
|
|
||||||
|
|
||||||
|
32
src/main.rs
32
src/main.rs
@ -1,3 +1,5 @@
|
|||||||
|
#![windows_subsystem = "windows"]
|
||||||
|
|
||||||
extern crate cairo;
|
extern crate cairo;
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
extern crate gdk;
|
extern crate gdk;
|
||||||
@ -11,13 +13,19 @@ extern crate gtk;
|
|||||||
extern crate gtk_sys;
|
extern crate gtk_sys;
|
||||||
extern crate htmlescape;
|
extern crate htmlescape;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
extern crate neovim_lib;
|
extern crate neovim_lib;
|
||||||
extern crate pango;
|
extern crate pango;
|
||||||
extern crate pango_cairo_sys;
|
extern crate pango_cairo_sys;
|
||||||
extern crate pango_sys;
|
extern crate pango_sys;
|
||||||
extern crate pangocairo;
|
extern crate pangocairo;
|
||||||
|
extern crate percent_encoding;
|
||||||
extern crate phf;
|
extern crate phf;
|
||||||
|
extern crate rmpv;
|
||||||
|
extern crate regex;
|
||||||
|
extern crate unicode_width;
|
||||||
|
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@ -43,11 +51,15 @@ mod shell;
|
|||||||
mod input;
|
mod input;
|
||||||
mod settings;
|
mod settings;
|
||||||
mod cursor;
|
mod cursor;
|
||||||
|
mod cmd_line;
|
||||||
mod shell_dlg;
|
mod shell_dlg;
|
||||||
mod popup_menu;
|
mod popup_menu;
|
||||||
mod project;
|
mod project;
|
||||||
mod tabline;
|
mod tabline;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod file_browser;
|
||||||
|
mod subscriptions;
|
||||||
|
mod misc;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@ -96,21 +108,23 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn open(app: >k::Application, files: &[gio::File], _: &str) {
|
fn open(app: >k::Application, files: &[gio::File], _: &str) {
|
||||||
for f in files {
|
let files_list: Vec<String> = files
|
||||||
let mut ui = Ui::new(ShellOptions::new(
|
.into_iter()
|
||||||
nvim_bin_path(std::env::args()),
|
.filter_map(|f| f.get_path()?.to_str().map(str::to_owned))
|
||||||
f.get_path().and_then(|p| p.to_str().map(str::to_owned)),
|
.collect();
|
||||||
nvim_timeout(std::env::args()),
|
let mut ui = Ui::new(ShellOptions::new(
|
||||||
));
|
nvim_bin_path(std::env::args()),
|
||||||
|
files_list,
|
||||||
|
nvim_timeout(std::env::args()),
|
||||||
|
));
|
||||||
|
|
||||||
ui.init(app, !nvim_disable_win_state(std::env::args()));
|
ui.init(app, !nvim_disable_win_state(std::env::args()));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn activate(app: >k::Application) {
|
fn activate(app: >k::Application) {
|
||||||
let mut ui = Ui::new(ShellOptions::new(
|
let mut ui = Ui::new(ShellOptions::new(
|
||||||
nvim_bin_path(std::env::args()),
|
nvim_bin_path(std::env::args()),
|
||||||
None,
|
Vec::new(),
|
||||||
nvim_timeout(std::env::args()),
|
nvim_timeout(std::env::args()),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
38
src/misc.rs
Normal file
38
src/misc.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use regex::Regex;
|
||||||
|
use percent_encoding::percent_decode;
|
||||||
|
|
||||||
|
/// Escape special ASCII characters with a backslash.
|
||||||
|
pub fn escape_filename<'t>(filename: &'t str) -> Cow<'t, str> {
|
||||||
|
lazy_static! {
|
||||||
|
static ref SPECIAL_CHARS: Regex = if cfg!(target_os = "windows") {
|
||||||
|
// On Windows, don't escape `:` and `\`, as these are valid components of the path.
|
||||||
|
Regex::new(r"[[:ascii:]&&[^0-9a-zA-Z._:\\-]]").unwrap()
|
||||||
|
} else {
|
||||||
|
// Similarly, don't escape `/` on other platforms.
|
||||||
|
Regex::new(r"[[:ascii:]&&[^0-9a-zA-Z._/-]]").unwrap()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
SPECIAL_CHARS.replace_all(&*filename, r"\$0")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decode a file URI.
|
||||||
|
///
|
||||||
|
/// - On UNIX: `file:///path/to/a%20file.ext` -> `/path/to/a file.ext`
|
||||||
|
/// - On Windows: `file:///C:/path/to/a%20file.ext` -> `C:\path\to\a file.ext`
|
||||||
|
pub fn decode_uri(uri: &str) -> Option<String> {
|
||||||
|
let path = match uri.split_at(8) {
|
||||||
|
("file:///", path) => path,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
let path = percent_decode(path.as_bytes()).decode_utf8().ok()?;
|
||||||
|
if cfg!(target_os = "windows") {
|
||||||
|
lazy_static! {
|
||||||
|
static ref SLASH: Regex = Regex::new(r"/").unwrap();
|
||||||
|
}
|
||||||
|
Some(String::from(SLASH.replace_all(&*path, r"\")))
|
||||||
|
} else {
|
||||||
|
Some("/".to_owned() + &path)
|
||||||
|
}
|
||||||
|
}
|
@ -36,9 +36,7 @@ impl<'a> NeovimRef<'a> {
|
|||||||
pub fn non_blocked(mut self) -> Option<Self> {
|
pub fn non_blocked(mut self) -> Option<Self> {
|
||||||
self.get_mode().ok_and_report().and_then(|mode| {
|
self.get_mode().ok_and_report().and_then(|mode| {
|
||||||
mode.iter()
|
mode.iter()
|
||||||
.find(|kv| {
|
.find(|kv| kv.0.as_str().map(|key| key == "blocking").unwrap_or(false))
|
||||||
kv.0.as_str().map(|key| key == "blocking").unwrap_or(false)
|
|
||||||
})
|
|
||||||
.map(|kv| kv.1.as_bool().unwrap_or(false))
|
.map(|kv| kv.1.as_bool().unwrap_or(false))
|
||||||
.and_then(|block| if block { None } else { Some(self) })
|
.and_then(|block| if block { None } else { Some(self) })
|
||||||
})
|
})
|
||||||
@ -71,7 +69,9 @@ pub struct NeovimClientAsync {
|
|||||||
|
|
||||||
impl NeovimClientAsync {
|
impl NeovimClientAsync {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
NeovimClientAsync { nvim: Arc::new(Mutex::new(None)) }
|
NeovimClientAsync {
|
||||||
|
nvim: Arc::new(Mutex::new(None)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn borrow(&self) -> Option<NeovimRef> {
|
pub fn borrow(&self) -> Option<NeovimRef> {
|
||||||
@ -81,7 +81,9 @@ impl NeovimClientAsync {
|
|||||||
|
|
||||||
impl Clone for NeovimClientAsync {
|
impl Clone for NeovimClientAsync {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
NeovimClientAsync { nvim: self.nvim.clone() }
|
NeovimClientAsync {
|
||||||
|
nvim: self.nvim.clone(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,9 +149,9 @@ impl NeovimClient {
|
|||||||
pub fn nvim(&self) -> Option<NeovimRef> {
|
pub fn nvim(&self) -> Option<NeovimRef> {
|
||||||
let nvim = self.nvim.borrow_mut();
|
let nvim = self.nvim.borrow_mut();
|
||||||
if nvim.is_some() {
|
if nvim.is_some() {
|
||||||
Some(NeovimRef::from_nvim(
|
Some(NeovimRef::from_nvim(RefMut::map(nvim, |n| {
|
||||||
RefMut::map(nvim, |n| n.as_mut().unwrap()),
|
n.as_mut().unwrap()
|
||||||
))
|
})))
|
||||||
} else {
|
} else {
|
||||||
self.nvim_async.borrow()
|
self.nvim_async.borrow()
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
use std::result;
|
use std::result;
|
||||||
|
|
||||||
use neovim_lib::CallError;
|
use neovim_lib::CallError;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::{Arc, mpsc};
|
use std::sync::{mpsc, Arc};
|
||||||
|
|
||||||
use neovim_lib::{Handler, Value};
|
use neovim_lib::{Handler, Value};
|
||||||
|
|
||||||
@ -9,53 +9,63 @@ use glib;
|
|||||||
|
|
||||||
use super::repaint_mode::RepaintMode;
|
use super::repaint_mode::RepaintMode;
|
||||||
use super::redraw_handler;
|
use super::redraw_handler;
|
||||||
use super::redraw_handler::RedrawEvents;
|
|
||||||
|
|
||||||
pub struct NvimHandler {
|
pub struct NvimHandler {
|
||||||
shell: Arc<UiMutex<shell::State>>,
|
shell: Arc<UiMutex<shell::State>>,
|
||||||
|
|
||||||
|
delayed_redraw_event_id: Arc<UiMutex<Option<glib::SourceId>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NvimHandler {
|
impl NvimHandler {
|
||||||
pub fn new(shell: Arc<UiMutex<shell::State>>) -> NvimHandler {
|
pub fn new(shell: Arc<UiMutex<shell::State>>) -> NvimHandler {
|
||||||
NvimHandler { shell: shell }
|
NvimHandler {
|
||||||
|
shell,
|
||||||
|
delayed_redraw_event_id: Arc::new(UiMutex::new(None)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nvim_cb(&self, method: &str, params: Vec<Value>) {
|
pub fn schedule_redraw_event(&self, event: Value) {
|
||||||
|
let shell = self.shell.clone();
|
||||||
|
let delayed_redraw_event_id = self.delayed_redraw_event_id.clone();
|
||||||
|
|
||||||
|
glib::idle_add(move || {
|
||||||
|
let id = Some(glib::timeout_add(
|
||||||
|
250,
|
||||||
|
clone!(shell, event, delayed_redraw_event_id => move || {
|
||||||
|
delayed_redraw_event_id.replace(None);
|
||||||
|
|
||||||
|
if let Err(msg) = call_redraw_handler(vec![event.clone()], &shell) {
|
||||||
|
error!("Error call function: {}", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
glib::Continue(false)
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
|
||||||
|
delayed_redraw_event_id.replace(id);
|
||||||
|
|
||||||
|
glib::Continue(false)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_scheduled_redraw_event(&self) {
|
||||||
|
let delayed_redraw_event_id = self.delayed_redraw_event_id.clone();
|
||||||
|
glib::idle_add(move || {
|
||||||
|
let id = delayed_redraw_event_id.replace(None);
|
||||||
|
if let Some(ev_id) = id {
|
||||||
|
glib::source_remove(ev_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
glib::Continue(false)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nvim_cb(&self, method: &str, mut params: Vec<Value>) {
|
||||||
match method {
|
match method {
|
||||||
"redraw" => {
|
"redraw" => {
|
||||||
self.safe_call(move |ui| {
|
redraw_handler::remove_or_delay_uneeded_events(self, &mut params);
|
||||||
let ui = &mut ui.borrow_mut();
|
|
||||||
let mut repaint_mode = RepaintMode::Nothing;
|
|
||||||
|
|
||||||
for ev in params {
|
self.safe_call(move |ui| call_redraw_handler(params, ui));
|
||||||
if let Value::Array(ev_args) = ev {
|
|
||||||
let mut args_iter = ev_args.into_iter();
|
|
||||||
let ev_name = args_iter.next();
|
|
||||||
if let Some(ev_name) = ev_name {
|
|
||||||
if let Some(ev_name) = ev_name.as_str() {
|
|
||||||
for local_args in args_iter {
|
|
||||||
let args = match local_args {
|
|
||||||
Value::Array(ar) => ar,
|
|
||||||
_ => vec![],
|
|
||||||
};
|
|
||||||
let call_reapint_mode =
|
|
||||||
redraw_handler::call(ui, &ev_name, &args)?;
|
|
||||||
repaint_mode = repaint_mode.join(call_reapint_mode);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error!("Unsupported event");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error!("Event name does not exists");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error!("Unsupported event type {:?}", ev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.on_redraw(&repaint_mode);
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
"Gui" => {
|
"Gui" => {
|
||||||
if !params.is_empty() {
|
if !params.is_empty() {
|
||||||
@ -67,8 +77,10 @@ impl NvimHandler {
|
|||||||
let ui = &mut ui.borrow_mut();
|
let ui = &mut ui.borrow_mut();
|
||||||
redraw_handler::call_gui_event(
|
redraw_handler::call_gui_event(
|
||||||
ui,
|
ui,
|
||||||
ev_name.as_str().ok_or_else(|| "Event name does not exists")?,
|
ev_name
|
||||||
&args,
|
.as_str()
|
||||||
|
.ok_or_else(|| "Event name does not exists")?,
|
||||||
|
args,
|
||||||
)?;
|
)?;
|
||||||
ui.on_redraw(&RepaintMode::All);
|
ui.on_redraw(&RepaintMode::All);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -83,13 +95,19 @@ impl NvimHandler {
|
|||||||
error!("Unsupported event {:?}", params);
|
error!("Unsupported event {:?}", params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"subscription" => {
|
||||||
|
self.safe_call(move |ui| {
|
||||||
|
let ui = &ui.borrow();
|
||||||
|
ui.notify(params)
|
||||||
|
});
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
error!("Notification {}({:?})", method, params);
|
error!("Notification {}({:?})", method, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nvim_cb_req (&self, method: &str, params: Vec<Value>) -> result::Result<Value, Value> {
|
fn nvim_cb_req(&self, method: &str, params: Vec<Value>) -> result::Result<Value, Value> {
|
||||||
match method {
|
match method {
|
||||||
"Gui" => {
|
"Gui" => {
|
||||||
if !params.is_empty() {
|
if !params.is_empty() {
|
||||||
@ -99,11 +117,15 @@ impl NvimHandler {
|
|||||||
let args = params_iter.collect();
|
let args = params_iter.collect();
|
||||||
let (sender, receiver) = mpsc::channel();
|
let (sender, receiver) = mpsc::channel();
|
||||||
self.safe_call(move |ui| {
|
self.safe_call(move |ui| {
|
||||||
sender.send(redraw_handler::call_gui_request(
|
sender
|
||||||
&ui.clone(),
|
.send(redraw_handler::call_gui_request(
|
||||||
req_name.as_str().ok_or_else(|| "Event name does not exists")?,
|
&ui.clone(),
|
||||||
&args,
|
req_name
|
||||||
)).unwrap();
|
.as_str()
|
||||||
|
.ok_or_else(|| "Event name does not exists")?,
|
||||||
|
&args,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
{
|
{
|
||||||
let ui = &mut ui.borrow_mut();
|
let ui = &mut ui.borrow_mut();
|
||||||
ui.on_redraw(&RepaintMode::All);
|
ui.on_redraw(&RepaintMode::All);
|
||||||
@ -123,29 +145,71 @@ impl NvimHandler {
|
|||||||
error!("Unsupported request {:?}", params);
|
error!("Unsupported request {:?}", params);
|
||||||
Err(Value::Nil)
|
Err(Value::Nil)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
error!("Request {}({:?})", method, params);
|
error!("Request {}({:?})", method, params);
|
||||||
Err(Value::Nil)
|
Err(Value::Nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn safe_call<F>(&self, cb: F)
|
fn safe_call<F>(&self, cb: F)
|
||||||
where
|
where
|
||||||
F: FnOnce(&Arc<UiMutex<shell::State>>) -> result::Result<(), String> + 'static + Send,
|
F: FnOnce(&Arc<UiMutex<shell::State>>) -> result::Result<(), String> + 'static + Send,
|
||||||
{
|
{
|
||||||
let mut cb = Some(cb);
|
safe_call(self.shell.clone(), cb);
|
||||||
let shell = self.shell.clone();
|
|
||||||
glib::idle_add(move || {
|
|
||||||
if let Err(msg) = cb.take().unwrap()(&shell) {
|
|
||||||
error!("Error call function: {}", msg);
|
|
||||||
}
|
|
||||||
glib::Continue(false)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn call_redraw_handler(
|
||||||
|
params: Vec<Value>,
|
||||||
|
ui: &Arc<UiMutex<shell::State>>,
|
||||||
|
) -> result::Result<(), String> {
|
||||||
|
let ui = &mut ui.borrow_mut();
|
||||||
|
let mut repaint_mode = RepaintMode::Nothing;
|
||||||
|
|
||||||
|
for ev in params {
|
||||||
|
if let Value::Array(ev_args) = ev {
|
||||||
|
let mut args_iter = ev_args.into_iter();
|
||||||
|
let ev_name = args_iter.next();
|
||||||
|
if let Some(ev_name) = ev_name {
|
||||||
|
if let Some(ev_name) = ev_name.as_str() {
|
||||||
|
for local_args in args_iter {
|
||||||
|
let args = match local_args {
|
||||||
|
Value::Array(ar) => ar,
|
||||||
|
_ => vec![],
|
||||||
|
};
|
||||||
|
let call_reapint_mode = redraw_handler::call(ui, &ev_name, args)?;
|
||||||
|
repaint_mode = repaint_mode.join(call_reapint_mode);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("Unsupported event");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("Event name does not exists");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error!("Unsupported event type {:?}", ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.on_redraw(&repaint_mode);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn safe_call<F>(shell: Arc<UiMutex<shell::State>>, cb: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(&Arc<UiMutex<shell::State>>) -> result::Result<(), String> + 'static + Send,
|
||||||
|
{
|
||||||
|
let mut cb = Some(cb);
|
||||||
|
glib::idle_add(move || {
|
||||||
|
if let Err(msg) = cb.take().unwrap()(&shell) {
|
||||||
|
error!("Error call function: {}", msg);
|
||||||
|
}
|
||||||
|
glib::Continue(false)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
impl Handler for NvimHandler {
|
impl Handler for NvimHandler {
|
||||||
fn handle_notify(&mut self, name: &str, args: Vec<Value>) {
|
fn handle_notify(&mut self, name: &str, args: Vec<Value>) {
|
||||||
self.nvim_cb(name, args);
|
self.nvim_cb(name, args);
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
mod client;
|
mod client;
|
||||||
mod handler;
|
mod handler;
|
||||||
mod mode_info;
|
mod mode_info;
|
||||||
@ -6,24 +5,23 @@ mod redraw_handler;
|
|||||||
mod repaint_mode;
|
mod repaint_mode;
|
||||||
mod ext;
|
mod ext;
|
||||||
|
|
||||||
pub use self::redraw_handler::{RedrawEvents, GuiApi, CompleteItem};
|
pub use self::redraw_handler::CompleteItem;
|
||||||
pub use self::repaint_mode::RepaintMode;
|
pub use self::repaint_mode::RepaintMode;
|
||||||
pub use self::client::{NeovimClient, NeovimClientAsync, NeovimRef};
|
pub use self::client::{NeovimClient, NeovimClientAsync, NeovimRef};
|
||||||
pub use self::mode_info::{ModeInfo, CursorShape};
|
pub use self::mode_info::{CursorShape, ModeInfo};
|
||||||
pub use self::ext::ErrorReport;
|
pub use self::ext::ErrorReport;
|
||||||
|
pub use self::handler::NvimHandler;
|
||||||
|
|
||||||
use std::error;
|
use std::error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::process::{Stdio, Command};
|
use std::process::{Command, Stdio};
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use neovim_lib::{Neovim, NeovimApi, Session, UiAttachOptions};
|
use neovim_lib::{Neovim, NeovimApi, NeovimApiAsync, Session, UiAttachOptions};
|
||||||
|
|
||||||
use ui::UiMutex;
|
use misc::escape_filename;
|
||||||
use shell;
|
|
||||||
use nvim_config::NvimConfig;
|
use nvim_config::NvimConfig;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -78,8 +76,14 @@ impl error::Error for NvimInitError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn set_windows_creation_flags(cmd: &mut Command) {
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
|
cmd.creation_flags(0x08000000); // CREATE_NO_WINDOW
|
||||||
|
}
|
||||||
|
|
||||||
pub fn start(
|
pub fn start(
|
||||||
shell: Arc<UiMutex<shell::State>>,
|
handler: NvimHandler,
|
||||||
nvim_bin_path: Option<&String>,
|
nvim_bin_path: Option<&String>,
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
) -> result::Result<Neovim, NvimInitError> {
|
) -> result::Result<Neovim, NvimInitError> {
|
||||||
@ -99,15 +103,15 @@ pub fn start(
|
|||||||
.arg("let g:GtkGuiLoaded = 1")
|
.arg("let g:GtkGuiLoaded = 1")
|
||||||
.stderr(Stdio::inherit());
|
.stderr(Stdio::inherit());
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
set_windows_creation_flags(&mut cmd);
|
||||||
|
|
||||||
if let Ok(runtime_path) = env::var("NVIM_GTK_RUNTIME_PATH") {
|
if let Ok(runtime_path) = env::var("NVIM_GTK_RUNTIME_PATH") {
|
||||||
cmd.arg("--cmd").arg(
|
cmd.arg("--cmd")
|
||||||
format!("let &rtp.=',{}'", runtime_path),
|
.arg(format!("let &rtp.=',{}'", runtime_path));
|
||||||
);
|
|
||||||
} else if let Some(prefix) = option_env!("PREFIX") {
|
} else if let Some(prefix) = option_env!("PREFIX") {
|
||||||
cmd.arg("--cmd").arg(format!(
|
cmd.arg("--cmd")
|
||||||
"let &rtp.=',{}/share/nvim-gtk/runtime'",
|
.arg(format!("let &rtp.=',{}/share/nvim-gtk/runtime'", prefix));
|
||||||
prefix
|
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
cmd.arg("--cmd").arg("let &rtp.=',runtime'");
|
cmd.arg("--cmd").arg("let &rtp.=',runtime'");
|
||||||
}
|
}
|
||||||
@ -129,38 +133,46 @@ pub fn start(
|
|||||||
|
|
||||||
let mut nvim = Neovim::new(session);
|
let mut nvim = Neovim::new(session);
|
||||||
|
|
||||||
nvim.session.start_event_loop_handler(
|
nvim.session.start_event_loop_handler(handler);
|
||||||
handler::NvimHandler::new(shell),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(nvim)
|
Ok(nvim)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post_start_init(
|
pub fn post_start_init(
|
||||||
nvim: NeovimClientAsync,
|
nvim: NeovimClientAsync,
|
||||||
open_path: Option<&String>,
|
open_paths: Vec<String>,
|
||||||
cols: u64,
|
cols: u64,
|
||||||
rows: u64,
|
rows: u64,
|
||||||
) -> result::Result<(), NvimInitError> {
|
) -> result::Result<(), NvimInitError> {
|
||||||
let mut opts = UiAttachOptions::new();
|
|
||||||
opts.set_popupmenu_external(true);
|
|
||||||
opts.set_tabline_external(true);
|
|
||||||
nvim.borrow()
|
nvim.borrow()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.ui_attach(cols, rows, &opts)
|
.ui_attach(
|
||||||
|
cols,
|
||||||
|
rows,
|
||||||
|
UiAttachOptions::new()
|
||||||
|
.set_popupmenu_external(true)
|
||||||
|
.set_tabline_external(true),
|
||||||
|
)
|
||||||
.map_err(NvimInitError::new_post_init)?;
|
.map_err(NvimInitError::new_post_init)?;
|
||||||
|
|
||||||
nvim.borrow()
|
nvim.borrow()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.command("runtime! ginit.vim")
|
.command("runtime! ginit.vim")
|
||||||
.map_err(NvimInitError::new_post_init)?;
|
.map_err(NvimInitError::new_post_init)?;
|
||||||
|
|
||||||
if let Some(path) = open_path {
|
if !open_paths.is_empty() {
|
||||||
|
let command = open_paths
|
||||||
|
.iter()
|
||||||
|
.fold(":ar".to_owned(), |command, filename| {
|
||||||
|
let filename = escape_filename(filename);
|
||||||
|
command + " " + &filename
|
||||||
|
});
|
||||||
nvim.borrow()
|
nvim.borrow()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.command(&format!("e {}", path))
|
.command_async(&command)
|
||||||
.map_err(NvimInitError::new_post_init)?;
|
.cb(|r| r.report_err())
|
||||||
|
.call();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,9 +12,9 @@ pub enum CursorShape {
|
|||||||
|
|
||||||
impl CursorShape {
|
impl CursorShape {
|
||||||
fn new(shape_code: &Value) -> Result<CursorShape, String> {
|
fn new(shape_code: &Value) -> Result<CursorShape, String> {
|
||||||
let str_code = shape_code.as_str().ok_or_else(|| {
|
let str_code = shape_code
|
||||||
"Can't convert cursor shape to string".to_owned()
|
.as_str()
|
||||||
})?;
|
.ok_or_else(|| "Can't convert cursor shape to string".to_owned())?;
|
||||||
|
|
||||||
Ok(match str_code {
|
Ok(match str_code {
|
||||||
"block" => CursorShape::Block,
|
"block" => CursorShape::Block,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::result;
|
use std::result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use neovim_lib::{Value, UiOption};
|
use neovim_lib::{UiOption, Value};
|
||||||
use neovim_lib::neovim_api::Tabpage;
|
use neovim_lib::neovim_api::Tabpage;
|
||||||
|
|
||||||
use ui::UiMutex;
|
use ui::UiMutex;
|
||||||
@ -9,69 +9,11 @@ use shell;
|
|||||||
use gtk::ClipboardExt;
|
use gtk::ClipboardExt;
|
||||||
|
|
||||||
use value::ValueMapExt;
|
use value::ValueMapExt;
|
||||||
|
use rmpv;
|
||||||
|
|
||||||
use super::repaint_mode::RepaintMode;
|
use super::repaint_mode::RepaintMode;
|
||||||
use super::mode_info::ModeInfo;
|
use super::mode_info::ModeInfo;
|
||||||
|
use super::handler::NvimHandler;
|
||||||
pub trait RedrawEvents {
|
|
||||||
fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode;
|
|
||||||
|
|
||||||
fn on_put(&mut self, text: &str) -> RepaintMode;
|
|
||||||
|
|
||||||
fn on_clear(&mut self) -> RepaintMode;
|
|
||||||
|
|
||||||
fn on_resize(&mut self, columns: u64, rows: u64) -> RepaintMode;
|
|
||||||
|
|
||||||
fn on_redraw(&mut self, mode: &RepaintMode);
|
|
||||||
|
|
||||||
fn on_highlight_set(&mut self, attrs: &[(Value, Value)]) -> RepaintMode;
|
|
||||||
|
|
||||||
fn on_eol_clear(&mut self) -> RepaintMode;
|
|
||||||
|
|
||||||
fn on_set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64) -> RepaintMode;
|
|
||||||
|
|
||||||
fn on_scroll(&mut self, count: i64) -> RepaintMode;
|
|
||||||
|
|
||||||
fn on_update_bg(&mut self, bg: i64) -> RepaintMode;
|
|
||||||
|
|
||||||
fn on_update_fg(&mut self, fg: i64) -> RepaintMode;
|
|
||||||
|
|
||||||
fn on_update_sp(&mut self, sp: i64) -> RepaintMode;
|
|
||||||
|
|
||||||
fn on_mode_change(&mut self, mode: &str, idx: u64) -> RepaintMode;
|
|
||||||
|
|
||||||
fn on_mouse(&mut self, on: bool) -> RepaintMode;
|
|
||||||
|
|
||||||
fn on_busy(&mut self, busy: bool) -> RepaintMode;
|
|
||||||
|
|
||||||
fn popupmenu_show(
|
|
||||||
&mut self,
|
|
||||||
menu: &[CompleteItem],
|
|
||||||
selected: i64,
|
|
||||||
row: u64,
|
|
||||||
col: u64,
|
|
||||||
) -> RepaintMode;
|
|
||||||
|
|
||||||
fn popupmenu_hide(&mut self) -> RepaintMode;
|
|
||||||
|
|
||||||
fn popupmenu_select(&mut self, selected: i64) -> RepaintMode;
|
|
||||||
|
|
||||||
fn tabline_update(
|
|
||||||
&mut self,
|
|
||||||
selected: Tabpage,
|
|
||||||
tabs: Vec<(Tabpage, Option<String>)>,
|
|
||||||
) -> RepaintMode;
|
|
||||||
|
|
||||||
fn mode_info_set(
|
|
||||||
&mut self,
|
|
||||||
cursor_style_enabled: bool,
|
|
||||||
mode_info: Vec<ModeInfo>,
|
|
||||||
) -> RepaintMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait GuiApi {
|
|
||||||
fn set_font(&mut self, font_desc: &str);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! try_str {
|
macro_rules! try_str {
|
||||||
($exp:expr) => ($exp.as_str().ok_or_else(|| "Can't convert argument to string".to_owned())?)
|
($exp:expr) => ($exp.as_str().ok_or_else(|| "Can't convert argument to string".to_owned())?)
|
||||||
@ -106,45 +48,75 @@ macro_rules! map_array {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! try_arg {
|
||||||
|
($value:expr, bool) => (try_bool!($value));
|
||||||
|
($value:expr, uint) => (try_uint!($value));
|
||||||
|
($value:expr, int) => (try_int!($value));
|
||||||
|
($value:expr, str) => (
|
||||||
|
match $value {
|
||||||
|
Value::String(s) => {
|
||||||
|
if let Some(s) = s.into_str() {
|
||||||
|
Ok(s)
|
||||||
|
} else {
|
||||||
|
Err("Can't convert to utf8 string".to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err("Can't convert to string".to_owned()),
|
||||||
|
}?);
|
||||||
|
($value:expr, ext) => (rmpv::ext::from_value($value).map_err(|e| e.to_string())?);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! call {
|
||||||
|
($s:ident -> $c:ident ($args:ident : $($arg_type:ident),+ )) => (
|
||||||
|
{
|
||||||
|
let mut iter = $args.into_iter();
|
||||||
|
$s.$c($(
|
||||||
|
try_arg!(iter.next()
|
||||||
|
.ok_or_else(|| format!("No such argument for {}", stringify!($c)))?,
|
||||||
|
$arg_type
|
||||||
|
)
|
||||||
|
),+ )
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn call_gui_event(
|
pub fn call_gui_event(
|
||||||
ui: &mut shell::State,
|
ui: &mut shell::State,
|
||||||
method: &str,
|
method: &str,
|
||||||
args: &Vec<Value>,
|
args: Vec<Value>,
|
||||||
) -> result::Result<(), String> {
|
) -> result::Result<(), String> {
|
||||||
match method {
|
match method {
|
||||||
"Font" => ui.set_font(try_str!(args[0])),
|
"Font" => call!(ui->set_font(args: str)),
|
||||||
"Clipboard" => {
|
"Clipboard" => match try_str!(args[0]) {
|
||||||
match try_str!(args[0]) {
|
"Set" => match try_str!(args[1]) {
|
||||||
"Set" => {
|
"*" => ui.clipboard_primary_set(try_str!(args[2])),
|
||||||
match try_str!(args[1]) {
|
_ => ui.clipboard_clipboard_set(try_str!(args[2])),
|
||||||
"*" => ui.clipboard_primary_set(try_str!(args[2])),
|
},
|
||||||
_ => ui.clipboard_clipboard_set(try_str!(args[2])),
|
opt => error!("Unknown option {}", opt),
|
||||||
}
|
|
||||||
},
|
|
||||||
opt => error!("Unknown option {}", opt),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"Option" => {
|
"Option" => match try_str!(args[0]) {
|
||||||
match try_str!(args[0]) {
|
"Popupmenu" => ui.nvim()
|
||||||
"Popupmenu" => {
|
.ok_or_else(|| "Nvim not initialized".to_owned())
|
||||||
ui.nvim()
|
.and_then(|mut nvim| {
|
||||||
.ok_or_else(|| "Nvim not initialized".to_owned())
|
nvim.set_option(UiOption::ExtPopupmenu(try_uint!(args[1]) == 1))
|
||||||
.and_then(|mut nvim| {
|
.map_err(|e| e.to_string())
|
||||||
nvim.set_option(UiOption::ExtPopupmenu(try_uint!(args[1]) == 1))
|
})?,
|
||||||
.map_err(|e| e.to_string())
|
"Tabline" => ui.nvim()
|
||||||
})?
|
.ok_or_else(|| "Nvim not initialized".to_owned())
|
||||||
}
|
.and_then(|mut nvim| {
|
||||||
"Tabline" => {
|
nvim.set_option(UiOption::ExtTabline(try_uint!(args[1]) == 1))
|
||||||
ui.nvim()
|
.map_err(|e| e.to_string())
|
||||||
.ok_or_else(|| "Nvim not initialized".to_owned())
|
})?,
|
||||||
.and_then(|mut nvim| {
|
"Cmdline" => ui.nvim()
|
||||||
nvim.set_option(UiOption::ExtTabline(try_uint!(args[1]) == 1))
|
.ok_or_else(|| "Nvim not initialized".to_owned())
|
||||||
.map_err(|e| e.to_string())
|
.and_then(|mut nvim| {
|
||||||
})?
|
nvim.set_option(UiOption::ExtCmdline(try_uint!(args[1]) == 1))
|
||||||
}
|
.map_err(|e| e.to_string())
|
||||||
opt => error!("Unknown option {}", opt),
|
})?,
|
||||||
}
|
opt => error!("Unknown option {}", opt),
|
||||||
|
},
|
||||||
|
"Command" => {
|
||||||
|
ui.on_command(args);
|
||||||
}
|
}
|
||||||
_ => return Err(format!("Unsupported event {}({:?})", method, args)),
|
_ => return Err(format!("Unsupported event {}({:?})", method, args)),
|
||||||
}
|
}
|
||||||
@ -171,61 +143,56 @@ pub fn call_gui_request(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let t = clipboard.wait_for_text().unwrap_or_else(|| String::new());
|
let t = clipboard.wait_for_text().unwrap_or_else(|| String::new());
|
||||||
Ok(Value::Array(t.split("\n").map(|s| s.into()).collect::<Vec<Value>>()))
|
Ok(Value::Array(
|
||||||
},
|
t.split("\n").map(|s| s.into()).collect::<Vec<Value>>(),
|
||||||
|
))
|
||||||
|
}
|
||||||
opt => {
|
opt => {
|
||||||
error!("Unknown option {}", opt);
|
error!("Unknown option {}", opt);
|
||||||
Err(Value::Nil)
|
Err(Value::Nil)
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => Err(Value::String(format!("Unsupported request {}({:?})", method, args).into())),
|
_ => Err(Value::String(
|
||||||
|
format!("Unsupported request {}({:?})", method, args).into(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call(
|
pub fn call(
|
||||||
ui: &mut shell::State,
|
ui: &mut shell::State,
|
||||||
method: &str,
|
method: &str,
|
||||||
args: &[Value],
|
args: Vec<Value>,
|
||||||
) -> result::Result<RepaintMode, String> {
|
) -> result::Result<RepaintMode, String> {
|
||||||
let repaint_mode = match method {
|
let repaint_mode = match method {
|
||||||
"cursor_goto" => ui.on_cursor_goto(try_uint!(args[0]), try_uint!(args[1])),
|
"cursor_goto" => call!(ui->on_cursor_goto(args: uint, uint)),
|
||||||
"put" => ui.on_put(try_str!(args[0])),
|
"put" => call!(ui->on_put(args: str)),
|
||||||
"clear" => ui.on_clear(),
|
"clear" => ui.on_clear(),
|
||||||
"resize" => ui.on_resize(try_uint!(args[0]), try_uint!(args[1])),
|
"resize" => call!(ui->on_resize(args: uint, uint)),
|
||||||
"highlight_set" => {
|
"highlight_set" => {
|
||||||
if let Value::Map(ref attrs) = args[0] {
|
call!(ui->on_highlight_set(args: ext));
|
||||||
ui.on_highlight_set(attrs);
|
|
||||||
} else {
|
|
||||||
panic!("Supports only map value as argument");
|
|
||||||
}
|
|
||||||
RepaintMode::Nothing
|
RepaintMode::Nothing
|
||||||
}
|
}
|
||||||
"eol_clear" => ui.on_eol_clear(),
|
"eol_clear" => ui.on_eol_clear(),
|
||||||
"set_scroll_region" => {
|
"set_scroll_region" => {
|
||||||
ui.on_set_scroll_region(
|
call!(ui->on_set_scroll_region(args: uint, uint, uint, uint));
|
||||||
try_uint!(args[0]),
|
|
||||||
try_uint!(args[1]),
|
|
||||||
try_uint!(args[2]),
|
|
||||||
try_uint!(args[3]),
|
|
||||||
);
|
|
||||||
RepaintMode::Nothing
|
RepaintMode::Nothing
|
||||||
}
|
}
|
||||||
"scroll" => ui.on_scroll(try_int!(args[0])),
|
"scroll" => call!(ui->on_scroll(args: int)),
|
||||||
"update_bg" => ui.on_update_bg(try_int!(args[0])),
|
"update_bg" => call!(ui->on_update_bg(args: int)),
|
||||||
"update_fg" => ui.on_update_fg(try_int!(args[0])),
|
"update_fg" => call!(ui->on_update_fg(args: int)),
|
||||||
"update_sp" => ui.on_update_sp(try_int!(args[0])),
|
"update_sp" => call!(ui->on_update_sp(args: int)),
|
||||||
"mode_change" => ui.on_mode_change(try_str!(args[0]), try_uint!(args[1])),
|
"mode_change" => call!(ui->on_mode_change(args: str, uint)),
|
||||||
"mouse_on" => ui.on_mouse(true),
|
"mouse_on" => ui.on_mouse(true),
|
||||||
"mouse_off" => ui.on_mouse(false),
|
"mouse_off" => ui.on_mouse(false),
|
||||||
"busy_start" => ui.on_busy(true),
|
"busy_start" => ui.on_busy(true),
|
||||||
"busy_stop" => ui.on_busy(false),
|
"busy_stop" => ui.on_busy(false),
|
||||||
"popupmenu_show" => {
|
"popupmenu_show" => {
|
||||||
let menu_items = map_array!(args[0], "Error get menu list array", |item| {
|
let menu_items = map_array!(args[0], "Error get menu list array", |item| map_array!(
|
||||||
map_array!(item, "Error get menu item array", |col| {
|
item,
|
||||||
col.as_str().ok_or("Error get menu column")
|
"Error get menu item array",
|
||||||
})
|
|col| col.as_str().ok_or("Error get menu column")
|
||||||
})?;
|
))?;
|
||||||
|
|
||||||
ui.popupmenu_show(
|
ui.popupmenu_show(
|
||||||
&CompleteItem::map(&menu_items),
|
&CompleteItem::map(&menu_items),
|
||||||
@ -235,16 +202,16 @@ pub fn call(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
"popupmenu_hide" => ui.popupmenu_hide(),
|
"popupmenu_hide" => ui.popupmenu_hide(),
|
||||||
"popupmenu_select" => ui.popupmenu_select(try_int!(args[0])),
|
"popupmenu_select" => call!(ui->popupmenu_select(args: int)),
|
||||||
"tabline_update" => {
|
"tabline_update" => {
|
||||||
let tabs_out = map_array!(args[1], "Error get tabline list".to_owned(), |tab| {
|
let tabs_out = map_array!(args[1], "Error get tabline list".to_owned(), |tab| {
|
||||||
tab.as_map()
|
tab.as_map()
|
||||||
.ok_or_else(|| "Error get map for tab".to_owned())
|
.ok_or_else(|| "Error get map for tab".to_owned())
|
||||||
.and_then(|tab_map| tab_map.to_attrs_map())
|
.and_then(|tab_map| tab_map.to_attrs_map())
|
||||||
.map(|tab_attrs| {
|
.map(|tab_attrs| {
|
||||||
let name_attr = tab_attrs.get("name").and_then(
|
let name_attr = tab_attrs
|
||||||
|n| n.as_str().map(|s| s.to_owned()),
|
.get("name")
|
||||||
);
|
.and_then(|n| n.as_str().map(|s| s.to_owned()));
|
||||||
let tab_attr = tab_attrs
|
let tab_attr = tab_attrs
|
||||||
.get("tab")
|
.get("tab")
|
||||||
.map(|&tab_id| Tabpage::new(tab_id.clone()))
|
.map(|&tab_id| Tabpage::new(tab_id.clone()))
|
||||||
@ -259,16 +226,21 @@ pub fn call(
|
|||||||
let mode_info = map_array!(
|
let mode_info = map_array!(
|
||||||
args[1],
|
args[1],
|
||||||
"Error get array key value for mode_info".to_owned(),
|
"Error get array key value for mode_info".to_owned(),
|
||||||
|mi| {
|
|mi| mi.as_map()
|
||||||
mi.as_map()
|
.ok_or_else(|| "Erro get map for mode_info".to_owned())
|
||||||
.ok_or_else(|| "Erro get map for mode_info".to_owned())
|
.and_then(|mi_map| ModeInfo::new(mi_map))
|
||||||
.and_then(|mi_map| ModeInfo::new(mi_map))
|
|
||||||
}
|
|
||||||
)?;
|
)?;
|
||||||
ui.mode_info_set(try_bool!(args[0]), mode_info)
|
ui.mode_info_set(try_bool!(args[0]), mode_info)
|
||||||
}
|
}
|
||||||
|
"cmdline_show" => call!(ui->cmdline_show(args: ext, uint, str, str, uint, uint)),
|
||||||
|
"cmdline_block_show" => call!(ui->cmdline_block_show(args: ext)),
|
||||||
|
"cmdline_block_append" => call!(ui->cmdline_block_append(args: ext)),
|
||||||
|
"cmdline_hide" => call!(ui->cmdline_hide(args: uint)),
|
||||||
|
"cmdline_block_hide" => ui.cmdline_block_hide(),
|
||||||
|
"cmdline_pos" => call!(ui->cmdline_pos(args: uint, uint)),
|
||||||
|
"cmdline_special_char" => call!(ui->cmdline_special_char(args: str, bool, uint)),
|
||||||
_ => {
|
_ => {
|
||||||
println!("Event {}({:?})", method, args);
|
warn!("Event {}({:?})", method, args);
|
||||||
RepaintMode::Nothing
|
RepaintMode::Nothing
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -276,6 +248,49 @@ pub fn call(
|
|||||||
Ok(repaint_mode)
|
Ok(repaint_mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Here two cases processed:
|
||||||
|
//
|
||||||
|
// 1. menu content update call popupmenu_hide followed by popupmenu_show in same batch
|
||||||
|
// this generates unneded hide event
|
||||||
|
// so in case we get both events, just romove one
|
||||||
|
//
|
||||||
|
// 2. hide event postpone in case show event come bit later
|
||||||
|
// but in new event batch
|
||||||
|
pub fn remove_or_delay_uneeded_events(handler: &NvimHandler, params: &mut Vec<Value>) {
|
||||||
|
let mut show_popup_finded = false;
|
||||||
|
let mut to_remove = Vec::new();
|
||||||
|
let mut delayed_hide_event = None;
|
||||||
|
|
||||||
|
for (idx, val) in params.iter().enumerate().rev() {
|
||||||
|
if let Some(args) = val.as_array() {
|
||||||
|
match args[0].as_str() {
|
||||||
|
Some("popupmenu_show") => {
|
||||||
|
show_popup_finded = true;
|
||||||
|
handler.remove_scheduled_redraw_event();
|
||||||
|
}
|
||||||
|
Some("popupmenu_hide") if !show_popup_finded && delayed_hide_event.is_none() => {
|
||||||
|
to_remove.push(idx);
|
||||||
|
delayed_hide_event = Some(idx);
|
||||||
|
handler.remove_scheduled_redraw_event();
|
||||||
|
}
|
||||||
|
Some("popupmenu_hide") => {
|
||||||
|
to_remove.push(idx);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
to_remove.iter().for_each(|&idx| {
|
||||||
|
let ev = params.remove(idx);
|
||||||
|
if let Some(delayed_hide_event_idx) = delayed_hide_event {
|
||||||
|
if delayed_hide_event_idx == idx {
|
||||||
|
handler.schedule_redraw_event(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub struct CompleteItem<'a> {
|
pub struct CompleteItem<'a> {
|
||||||
pub word: &'a str,
|
pub word: &'a str,
|
||||||
pub kind: &'a str,
|
pub kind: &'a str,
|
||||||
@ -286,13 +301,11 @@ pub struct CompleteItem<'a> {
|
|||||||
impl<'a> CompleteItem<'a> {
|
impl<'a> CompleteItem<'a> {
|
||||||
fn map(menu: &'a [Vec<&str>]) -> Vec<Self> {
|
fn map(menu: &'a [Vec<&str>]) -> Vec<Self> {
|
||||||
menu.iter()
|
menu.iter()
|
||||||
.map(|menu| {
|
.map(|menu| CompleteItem {
|
||||||
CompleteItem {
|
word: menu[0],
|
||||||
word: menu[0],
|
kind: menu[1],
|
||||||
kind: menu[1],
|
menu: menu[2],
|
||||||
menu: menu[2],
|
info: menu[3],
|
||||||
info: menu[3],
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,6 @@ impl RepaintMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use neovim_lib::NeovimApi;
|
use neovim_lib::{NeovimApi, NeovimApiAsync};
|
||||||
|
|
||||||
use nvim::{NeovimClient, ErrorReport, NeovimRef};
|
use nvim::{NeovimClient, ErrorReport, NeovimRef};
|
||||||
use value::ValueMapExt;
|
use value::ValueMapExt;
|
||||||
@ -84,7 +84,9 @@ impl Manager {
|
|||||||
|
|
||||||
pub fn reload(&self, path: &str) {
|
pub fn reload(&self, path: &str) {
|
||||||
if let Some(mut nvim) = self.nvim() {
|
if let Some(mut nvim) = self.nvim() {
|
||||||
nvim.command(&format!("source {}", path)).report_err();
|
nvim.command_async(&format!("source {}", path))
|
||||||
|
.cb(|r| r.report_err())
|
||||||
|
.call()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,9 +11,9 @@ use pango::{self, LayoutExt};
|
|||||||
use neovim_lib::{Neovim, NeovimApi};
|
use neovim_lib::{Neovim, NeovimApi};
|
||||||
|
|
||||||
use color::ColorModel;
|
use color::ColorModel;
|
||||||
use nvim::{self, ErrorReport, CompleteItem};
|
use nvim::{self, ErrorReport, NeovimClient};
|
||||||
use shell;
|
|
||||||
use input;
|
use input;
|
||||||
|
use render;
|
||||||
|
|
||||||
const MAX_VISIBLE_ROWS: i32 = 10;
|
const MAX_VISIBLE_ROWS: i32 = 10;
|
||||||
|
|
||||||
@ -32,6 +32,7 @@ struct State {
|
|||||||
impl State {
|
impl State {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let tree = gtk::TreeView::new();
|
let tree = gtk::TreeView::new();
|
||||||
|
tree.get_selection().set_mode(gtk::SelectionMode::Single);
|
||||||
let css_provider = gtk::CssProvider::new();
|
let css_provider = gtk::CssProvider::new();
|
||||||
|
|
||||||
let style_context = tree.get_style_context().unwrap();
|
let style_context = tree.get_style_context().unwrap();
|
||||||
@ -74,27 +75,26 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn before_show(&mut self, shell: &shell::State, menu_items: &[CompleteItem], selected: i64) {
|
fn before_show(&mut self, ctx: PopupMenuContext) {
|
||||||
if self.nvim.is_none() {
|
if self.nvim.is_none() {
|
||||||
self.nvim = Some(shell.nvim_clone());
|
self.nvim = Some(ctx.nvim.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let max_width = shell.drawing_area.get_allocated_width();
|
self.scroll.set_max_content_width(ctx.max_width);
|
||||||
self.scroll.set_max_content_width(max_width - 20);
|
|
||||||
self.scroll.set_propagate_natural_width(true);
|
self.scroll.set_propagate_natural_width(true);
|
||||||
self.update_tree(menu_items, shell);
|
self.update_tree(&ctx);
|
||||||
self.select(selected);
|
self.select(ctx.selected);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn limit_column_widths(&self, menu: &[CompleteItem], shell: &shell::State) {
|
fn limit_column_widths(&self, ctx: &PopupMenuContext) {
|
||||||
const DEFAULT_PADDING: i32 = 5;
|
const DEFAULT_PADDING: i32 = 5;
|
||||||
|
|
||||||
let layout = shell.font_ctx.create_layout();
|
let layout = ctx.font_ctx.create_layout();
|
||||||
let kind_exists = menu.iter().find(|i| i.kind.len() > 0).is_some();
|
let kind_exists = ctx.menu_items.iter().find(|i| i.kind.len() > 0).is_some();
|
||||||
let max_width = self.scroll.get_max_content_width();
|
let max_width = self.scroll.get_max_content_width();
|
||||||
let (xpad, _) = self.renderer.get_padding();
|
let (xpad, _) = self.renderer.get_padding();
|
||||||
|
|
||||||
let max_word_line = menu.iter().max_by_key(|m| m.word.len()).unwrap();
|
let max_word_line = ctx.menu_items.iter().max_by_key(|m| m.word.len()).unwrap();
|
||||||
layout.set_text(max_word_line.word);
|
layout.set_text(max_word_line.word);
|
||||||
let (word_max_width, _) = layout.get_pixel_size();
|
let (word_max_width, _) = layout.get_pixel_size();
|
||||||
let word_column_width = word_max_width + xpad * 2 + DEFAULT_PADDING;
|
let word_column_width = word_max_width + xpad * 2 + DEFAULT_PADDING;
|
||||||
@ -114,7 +114,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let max_menu_line = menu.iter().max_by_key(|m| m.menu.len()).unwrap();
|
let max_menu_line = ctx.menu_items.iter().max_by_key(|m| m.menu.len()).unwrap();
|
||||||
|
|
||||||
if max_menu_line.menu.len() > 0 {
|
if max_menu_line.menu.len() > 0 {
|
||||||
layout.set_text(max_menu_line.menu);
|
layout.set_text(max_menu_line.menu);
|
||||||
@ -126,31 +126,28 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_tree(&self, menu: &[CompleteItem], shell: &shell::State) {
|
fn update_tree(&self, ctx: &PopupMenuContext) {
|
||||||
if menu.is_empty() {
|
if ctx.menu_items.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.limit_column_widths(menu, shell);
|
self.limit_column_widths(ctx);
|
||||||
|
|
||||||
self.renderer.set_property_font(
|
self.renderer.set_property_font(
|
||||||
Some(&shell.get_font_desc().to_string()),
|
Some(&ctx.font_ctx.font_description().to_string()),
|
||||||
);
|
);
|
||||||
|
|
||||||
let color_model = &shell.color_model;
|
let color_model = &ctx.color_model;
|
||||||
self.renderer.set_property_foreground_rgba(
|
self.renderer.set_property_foreground_rgba(
|
||||||
Some(&color_model.pmenu_fg().into()),
|
Some(&color_model.pmenu_fg().into()),
|
||||||
);
|
);
|
||||||
self.renderer.set_property_background_rgba(
|
|
||||||
Some(&color_model.pmenu_bg().into()),
|
|
||||||
);
|
|
||||||
|
|
||||||
self.update_css(color_model);
|
self.update_css(color_model);
|
||||||
|
|
||||||
let list_store = gtk::ListStore::new(&vec![gtk::Type::String; 4]);
|
let list_store = gtk::ListStore::new(&vec![gtk::Type::String; 4]);
|
||||||
let all_column_ids: Vec<u32> = (0..4).map(|i| i as u32).collect();
|
let all_column_ids: Vec<u32> = (0..4).map(|i| i as u32).collect();
|
||||||
|
|
||||||
for line in menu {
|
for line in ctx.menu_items {
|
||||||
let line_array: [&glib::ToValue; 4] = [&line.word, &line.kind, &line.menu, &line.info];
|
let line_array: [&glib::ToValue; 4] = [&line.word, &line.kind, &line.menu, &line.info];
|
||||||
list_store.insert_with_values(None, &all_column_ids, &line_array[..]);
|
list_store.insert_with_values(None, &all_column_ids, &line_array[..]);
|
||||||
}
|
}
|
||||||
@ -165,9 +162,11 @@ impl State {
|
|||||||
match gtk::CssProviderExt::load_from_data(
|
match gtk::CssProviderExt::load_from_data(
|
||||||
&self.css_provider,
|
&self.css_provider,
|
||||||
&format!(
|
&format!(
|
||||||
".view {{ color: {}; background-color: {};}}",
|
".view :selected {{ color: {}; background-color: {};}}\n
|
||||||
|
.view {{ background-color: {}; }}",
|
||||||
fg.to_hex(),
|
fg.to_hex(),
|
||||||
bg.to_hex()
|
bg.to_hex(),
|
||||||
|
color_model.pmenu_bg().to_hex(),
|
||||||
).as_bytes(),
|
).as_bytes(),
|
||||||
) {
|
) {
|
||||||
Err(e) => error!("Can't update css {}", e),
|
Err(e) => error!("Can't update css {}", e),
|
||||||
@ -299,30 +298,16 @@ impl PopupMenu {
|
|||||||
self.open
|
self.open
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show(
|
pub fn show(&mut self, ctx: PopupMenuContext) {
|
||||||
&mut self,
|
|
||||||
shell: &shell::State,
|
|
||||||
menu_items: &[CompleteItem],
|
|
||||||
selected: i64,
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
width: i32,
|
|
||||||
height: i32,
|
|
||||||
) {
|
|
||||||
|
|
||||||
self.open = true;
|
self.open = true;
|
||||||
|
|
||||||
self.popover.set_pointing_to(>k::Rectangle {
|
self.popover.set_pointing_to(>k::Rectangle {
|
||||||
x,
|
x: ctx.x,
|
||||||
y,
|
y: ctx.y,
|
||||||
width,
|
width: ctx.width,
|
||||||
height,
|
height: ctx.height,
|
||||||
});
|
});
|
||||||
self.state.borrow_mut().before_show(
|
self.state.borrow_mut().before_show(ctx);
|
||||||
shell,
|
|
||||||
menu_items,
|
|
||||||
selected,
|
|
||||||
);
|
|
||||||
self.popover.popup()
|
self.popover.popup()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,6 +324,18 @@ impl PopupMenu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PopupMenuContext<'a> {
|
||||||
|
pub nvim: &'a Rc<NeovimClient>,
|
||||||
|
pub color_model: &'a ColorModel,
|
||||||
|
pub font_ctx: &'a render::Context,
|
||||||
|
pub menu_items: &'a [nvim::CompleteItem<'a>],
|
||||||
|
pub selected: i64,
|
||||||
|
pub x: i32,
|
||||||
|
pub y: i32,
|
||||||
|
pub width: i32,
|
||||||
|
pub height: i32,
|
||||||
|
pub max_width: i32,
|
||||||
|
}
|
||||||
|
|
||||||
fn tree_button_press(tree: >k::TreeView, ev: &EventButton, nvim: &mut Neovim) -> Inhibit {
|
fn tree_button_press(tree: >k::TreeView, ev: &EventButton, nvim: &mut Neovim) -> Inhibit {
|
||||||
if ev.get_event_type() != EventType::ButtonPress {
|
if ev.get_event_type() != EventType::ButtonPress {
|
||||||
|
@ -59,7 +59,7 @@ pub struct Projects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Projects {
|
impl Projects {
|
||||||
pub fn new(ref_widget: >k::ToolButton, shell: Rc<RefCell<Shell>>) -> Rc<RefCell<Projects>> {
|
pub fn new(ref_widget: >k::Button, shell: Rc<RefCell<Shell>>) -> Rc<RefCell<Projects>> {
|
||||||
let projects = Projects {
|
let projects = Projects {
|
||||||
shell,
|
shell,
|
||||||
popup: Popover::new(Some(ref_widget)),
|
popup: Popover::new(Some(ref_widget)),
|
||||||
@ -73,6 +73,9 @@ impl Projects {
|
|||||||
|
|
||||||
projects.setup_tree();
|
projects.setup_tree();
|
||||||
|
|
||||||
|
projects.tree.set_activate_on_single_click(true);
|
||||||
|
projects.tree.set_hover_selection(true);
|
||||||
|
projects.tree.set_grid_lines(gtk::TreeViewGridLines::Horizontal);
|
||||||
|
|
||||||
let vbox = gtk::Box::new(Orientation::Vertical, 5);
|
let vbox = gtk::Box::new(Orientation::Vertical, 5);
|
||||||
vbox.set_border_width(5);
|
vbox.set_border_width(5);
|
||||||
@ -89,11 +92,12 @@ impl Projects {
|
|||||||
);
|
);
|
||||||
|
|
||||||
projects.scroll.add(&projects.tree);
|
projects.scroll.add(&projects.tree);
|
||||||
|
projects.scroll.set_shadow_type(gtk::ShadowType::In);
|
||||||
|
|
||||||
vbox.pack_start(&projects.scroll, true, true, 0);
|
vbox.pack_start(&projects.scroll, true, true, 0);
|
||||||
|
|
||||||
let open_btn = gtk::Button::new_with_label("Other Documents…");
|
let open_btn = gtk::Button::new_with_label("Other Documents…");
|
||||||
vbox.pack_start(&open_btn, true, true, 0);
|
vbox.pack_start(&open_btn, true, true, 5);
|
||||||
|
|
||||||
vbox.show_all();
|
vbox.show_all();
|
||||||
projects.popup.add(&vbox);
|
projects.popup.add(&vbox);
|
||||||
@ -129,7 +133,12 @@ impl Projects {
|
|||||||
|
|
||||||
let prj_ref = projects.clone();
|
let prj_ref = projects.clone();
|
||||||
projects.borrow().tree.connect_row_activated(
|
projects.borrow().tree.connect_row_activated(
|
||||||
move |tree, _, _| {
|
move |tree, _, column| {
|
||||||
|
// Don't activate if the user clicked the checkbox.
|
||||||
|
let toggle_column = tree.get_column(2).unwrap();
|
||||||
|
if *column == toggle_column {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let selection = tree.get_selection();
|
let selection = tree.get_selection();
|
||||||
if let Some((model, iter)) = selection.get_selected() {
|
if let Some((model, iter)) = selection.get_selected() {
|
||||||
prj_ref.borrow().open_uri(&model, &iter);
|
prj_ref.borrow().open_uri(&model, &iter);
|
||||||
@ -281,6 +290,7 @@ impl Projects {
|
|||||||
let image_column = TreeViewColumn::new();
|
let image_column = TreeViewColumn::new();
|
||||||
|
|
||||||
let icon_renderer = CellRendererPixbuf::new();
|
let icon_renderer = CellRendererPixbuf::new();
|
||||||
|
icon_renderer.set_padding(5, 0);
|
||||||
image_column.pack_start(&icon_renderer, true);
|
image_column.pack_start(&icon_renderer, true);
|
||||||
|
|
||||||
image_column.add_attribute(
|
image_column.add_attribute(
|
||||||
@ -293,18 +303,23 @@ impl Projects {
|
|||||||
|
|
||||||
let text_column = TreeViewColumn::new();
|
let text_column = TreeViewColumn::new();
|
||||||
|
|
||||||
self.name_renderer.set_property_width_chars(60);
|
self.name_renderer.set_property_width_chars(45);
|
||||||
self.path_renderer.set_property_width_chars(60);
|
self.path_renderer.set_property_width_chars(45);
|
||||||
|
self.name_renderer.set_property_ellipsize(
|
||||||
|
pango::EllipsizeMode::Middle,
|
||||||
|
);
|
||||||
self.path_renderer.set_property_ellipsize(
|
self.path_renderer.set_property_ellipsize(
|
||||||
pango::EllipsizeMode::Start,
|
pango::EllipsizeMode::Start,
|
||||||
);
|
);
|
||||||
|
self.name_renderer.set_padding(0, 5);
|
||||||
|
self.path_renderer.set_padding(0, 5);
|
||||||
|
|
||||||
text_column.pack_start(&self.name_renderer, true);
|
text_column.pack_start(&self.name_renderer, true);
|
||||||
text_column.pack_start(&self.path_renderer, true);
|
text_column.pack_start(&self.path_renderer, true);
|
||||||
|
|
||||||
text_column.add_attribute(
|
text_column.add_attribute(
|
||||||
&self.name_renderer,
|
&self.name_renderer,
|
||||||
"markup",
|
"text",
|
||||||
ProjectViewColumns::Name as i32,
|
ProjectViewColumns::Name as i32,
|
||||||
);
|
);
|
||||||
text_column.add_attribute(
|
text_column.add_attribute(
|
||||||
@ -492,7 +507,7 @@ impl Entry {
|
|||||||
format!("<small>{}</small>", encode_minimal(&s.to_string_lossy()))
|
format!("<small>{}</small>", encode_minimal(&s.to_string_lossy()))
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| "".to_owned()),
|
.unwrap_or_else(|| "".to_owned()),
|
||||||
file_name: format!("<big>{}</big>", encode_minimal(name)),
|
file_name: encode_minimal(name),
|
||||||
name: name.to_owned(),
|
name: name.to_owned(),
|
||||||
pixbuf: BOOKMARKED_PIXBUF,
|
pixbuf: BOOKMARKED_PIXBUF,
|
||||||
project: true,
|
project: true,
|
||||||
@ -513,7 +528,7 @@ impl Entry {
|
|||||||
format!("<small>{}</small>", encode_minimal(&s.to_string_lossy()))
|
format!("<small>{}</small>", encode_minimal(&s.to_string_lossy()))
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| "".to_owned()),
|
.unwrap_or_else(|| "".to_owned()),
|
||||||
file_name: format!("<big>{}</big>", encode_minimal(&name)),
|
file_name: encode_minimal(&name),
|
||||||
name,
|
name,
|
||||||
pixbuf: CURRENT_DIR_PIXBUF,
|
pixbuf: CURRENT_DIR_PIXBUF,
|
||||||
project: true,
|
project: true,
|
||||||
@ -534,7 +549,7 @@ impl Entry {
|
|||||||
format!("<small>{}</small>", encode_minimal(&s.to_string_lossy()))
|
format!("<small>{}</small>", encode_minimal(&s.to_string_lossy()))
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| "".to_owned()),
|
.unwrap_or_else(|| "".to_owned()),
|
||||||
file_name: format!("<big>{}</big>", encode_minimal(&name)),
|
file_name: encode_minimal(&name),
|
||||||
name,
|
name,
|
||||||
pixbuf: PLAIN_FILE_PIXBUF,
|
pixbuf: PLAIN_FILE_PIXBUF,
|
||||||
project: false,
|
project: false,
|
||||||
|
@ -14,7 +14,9 @@ pub struct Context {
|
|||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
pub fn new(font_desc: pango::FontDescription) -> Self {
|
pub fn new(font_desc: pango::FontDescription) -> Self {
|
||||||
Context { state: ContextState::new(font_desc) }
|
Context {
|
||||||
|
state: ContextState::new(font_desc),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, font_desc: pango::FontDescription) {
|
pub fn update(&mut self, font_desc: pango::FontDescription) {
|
||||||
@ -88,20 +90,18 @@ pub struct CellMetrics {
|
|||||||
|
|
||||||
impl CellMetrics {
|
impl CellMetrics {
|
||||||
fn new(font_metrics: &pango::FontMetrics) -> Self {
|
fn new(font_metrics: &pango::FontMetrics) -> Self {
|
||||||
|
|
||||||
CellMetrics {
|
CellMetrics {
|
||||||
pango_ascent: font_metrics.get_ascent(),
|
pango_ascent: font_metrics.get_ascent(),
|
||||||
pango_descent: font_metrics.get_descent(),
|
pango_descent: font_metrics.get_descent(),
|
||||||
pango_char_width: font_metrics.get_approximate_digit_width(),
|
pango_char_width: font_metrics.get_approximate_digit_width(),
|
||||||
ascent: font_metrics.get_ascent() as f64 / pango::SCALE as f64,
|
ascent: font_metrics.get_ascent() as f64 / pango::SCALE as f64,
|
||||||
line_height: (font_metrics.get_ascent() + font_metrics.get_descent()) as f64 /
|
line_height: (font_metrics.get_ascent() + font_metrics.get_descent()) as f64
|
||||||
pango::SCALE as f64,
|
/ pango::SCALE as f64,
|
||||||
char_width: font_metrics.get_approximate_digit_width() as f64 / pango::SCALE as f64,
|
char_width: font_metrics.get_approximate_digit_width() as f64 / pango::SCALE as f64,
|
||||||
underline_position: (font_metrics.get_ascent() -
|
underline_position: (font_metrics.get_ascent() - font_metrics.get_underline_position())
|
||||||
font_metrics.get_underline_position()) as
|
as f64 / pango::SCALE as f64,
|
||||||
f64 / pango::SCALE as f64,
|
underline_thickness: font_metrics.get_underline_thickness() as f64
|
||||||
underline_thickness: font_metrics.get_underline_thickness() as f64 /
|
/ pango::SCALE as f64,
|
||||||
pango::SCALE as f64,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ mod model_clip_iterator;
|
|||||||
|
|
||||||
pub use self::context::Context;
|
pub use self::context::Context;
|
||||||
pub use self::context::CellMetrics;
|
pub use self::context::CellMetrics;
|
||||||
use self::model_clip_iterator::{RowView, ModelClipIteratorFactory};
|
use self::model_clip_iterator::{ModelClipIteratorFactory, RowView};
|
||||||
|
|
||||||
use mode;
|
use mode;
|
||||||
use color;
|
use color;
|
||||||
@ -14,24 +14,26 @@ use pango;
|
|||||||
use cairo;
|
use cairo;
|
||||||
use pangocairo;
|
use pangocairo;
|
||||||
|
|
||||||
use cursor;
|
use cursor::Cursor;
|
||||||
use ui_model;
|
use ui_model;
|
||||||
|
|
||||||
pub fn render(
|
pub fn clear(ctx: &cairo::Context, color_model: &color::ColorModel) {
|
||||||
ctx: &cairo::Context,
|
|
||||||
cursor: &cursor::Cursor,
|
|
||||||
font_ctx: &context::Context,
|
|
||||||
ui_model: &ui_model::UiModel,
|
|
||||||
color_model: &color::ColorModel,
|
|
||||||
mode: &mode::Mode,
|
|
||||||
) {
|
|
||||||
ctx.set_source_rgb(
|
ctx.set_source_rgb(
|
||||||
color_model.bg_color.0,
|
color_model.bg_color.0,
|
||||||
color_model.bg_color.1,
|
color_model.bg_color.1,
|
||||||
color_model.bg_color.2,
|
color_model.bg_color.2,
|
||||||
);
|
);
|
||||||
ctx.paint();
|
ctx.paint();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render<C: Cursor>(
|
||||||
|
ctx: &cairo::Context,
|
||||||
|
cursor: &C,
|
||||||
|
font_ctx: &context::Context,
|
||||||
|
ui_model: &ui_model::UiModel,
|
||||||
|
color_model: &color::ColorModel,
|
||||||
|
mode: &mode::Mode,
|
||||||
|
) {
|
||||||
let cell_metrics = font_ctx.cell_metrics();
|
let cell_metrics = font_ctx.cell_metrics();
|
||||||
let &CellMetrics { char_width, .. } = cell_metrics;
|
let &CellMetrics { char_width, .. } = cell_metrics;
|
||||||
let (cursor_row, cursor_col) = ui_model.get_cursor();
|
let (cursor_row, cursor_col) = ui_model.get_cursor();
|
||||||
@ -47,19 +49,19 @@ pub fn render(
|
|||||||
|
|
||||||
for cell_view in ui_model.get_clip_iterator(ctx, cell_metrics) {
|
for cell_view in ui_model.get_clip_iterator(ctx, cell_metrics) {
|
||||||
let mut line_x = 0.0;
|
let mut line_x = 0.0;
|
||||||
let RowView { line, row, line_y, .. } = cell_view;
|
let RowView {
|
||||||
|
line, row, line_y, ..
|
||||||
|
} = cell_view;
|
||||||
|
|
||||||
for (col, cell) in line.line.iter().enumerate() {
|
for (col, cell) in line.line.iter().enumerate() {
|
||||||
|
|
||||||
draw_cell(&cell_view, color_model, cell, col, line_x);
|
draw_cell(&cell_view, color_model, cell, col, line_x);
|
||||||
|
|
||||||
draw_underline(&cell_view, color_model, cell, line_x);
|
draw_underline(&cell_view, color_model, cell, line_x);
|
||||||
|
|
||||||
if row == cursor_row && col == cursor_col {
|
if row == cursor_row && col == cursor_col {
|
||||||
let double_width = line.line.get(col + 1).map_or(
|
let double_width = line.line
|
||||||
false,
|
.get(col + 1)
|
||||||
|c| c.attrs.double_width,
|
.map_or(false, |c| c.attrs.double_width);
|
||||||
);
|
|
||||||
ctx.move_to(line_x, line_y);
|
ctx.move_to(line_x, line_y);
|
||||||
cursor.draw(
|
cursor.draw(
|
||||||
ctx,
|
ctx,
|
||||||
@ -82,19 +84,18 @@ fn draw_underline(
|
|||||||
cell: &ui_model::Cell,
|
cell: &ui_model::Cell,
|
||||||
line_x: f64,
|
line_x: f64,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
if cell.attrs.underline || cell.attrs.undercurl {
|
if cell.attrs.underline || cell.attrs.undercurl {
|
||||||
|
|
||||||
let &RowView {
|
let &RowView {
|
||||||
ctx,
|
ctx,
|
||||||
line_y,
|
line_y,
|
||||||
cell_metrics: &CellMetrics {
|
cell_metrics:
|
||||||
line_height,
|
&CellMetrics {
|
||||||
char_width,
|
line_height,
|
||||||
underline_position,
|
char_width,
|
||||||
underline_thickness,
|
underline_position,
|
||||||
..
|
underline_thickness,
|
||||||
},
|
..
|
||||||
|
},
|
||||||
..
|
..
|
||||||
} = cell_view;
|
} = cell_view;
|
||||||
|
|
||||||
@ -129,11 +130,12 @@ fn draw_cell_bg(
|
|||||||
ctx,
|
ctx,
|
||||||
line,
|
line,
|
||||||
line_y,
|
line_y,
|
||||||
cell_metrics: &CellMetrics {
|
cell_metrics:
|
||||||
char_width,
|
&CellMetrics {
|
||||||
line_height,
|
char_width,
|
||||||
..
|
line_height,
|
||||||
},
|
..
|
||||||
|
},
|
||||||
..
|
..
|
||||||
} = cell_view;
|
} = cell_view;
|
||||||
|
|
||||||
@ -157,7 +159,6 @@ fn draw_cell_bg(
|
|||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_cell(
|
fn draw_cell(
|
||||||
@ -167,15 +168,11 @@ fn draw_cell(
|
|||||||
col: usize,
|
col: usize,
|
||||||
line_x: f64,
|
line_x: f64,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
let &RowView {
|
let &RowView {
|
||||||
ctx,
|
ctx,
|
||||||
line,
|
line,
|
||||||
line_y,
|
line_y,
|
||||||
cell_metrics: &CellMetrics {
|
cell_metrics: &CellMetrics { ascent, .. },
|
||||||
ascent,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
..
|
..
|
||||||
} = cell_view;
|
} = cell_view;
|
||||||
|
|
||||||
@ -188,7 +185,6 @@ fn draw_cell(
|
|||||||
|
|
||||||
show_glyph_string(ctx, item.font(), glyphs);
|
show_glyph_string(ctx, item.font(), glyphs);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
use std::cmp::min;
|
||||||
use std::slice::Iter;
|
use std::slice::Iter;
|
||||||
|
|
||||||
use cairo;
|
use cairo;
|
||||||
|
|
||||||
use super::context::CellMetrics;
|
use super::context::CellMetrics;
|
||||||
@ -76,17 +78,17 @@ impl ModelClipIteratorFactory for ui_model::UiModel {
|
|||||||
let model = self.model();
|
let model = self.model();
|
||||||
|
|
||||||
let (x1, y1, x2, y2) = ctx.clip_extents();
|
let (x1, y1, x2, y2) = ctx.clip_extents();
|
||||||
let model_clip = ui_model::ModelRect::from_area(cell_metrics, x1, y1, x2, y2);
|
|
||||||
let model_clip_top = if model_clip.top <= 0 {
|
// in case ctx.translate is used y1 can be less then 0
|
||||||
|
// in this case just use 0 as top value
|
||||||
|
let model_clip = ui_model::ModelRect::from_area(cell_metrics, x1, y1.max(0.0), x2, y2);
|
||||||
|
|
||||||
|
let model_clip_top = if model_clip.top == 0 {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
model_clip.top - 1
|
model_clip.top - 1
|
||||||
};
|
};
|
||||||
let model_clip_bot = if model_clip.bot >= model.len() - 1 {
|
let model_clip_bot = min(model.len() - 1, model_clip.bot + 1);
|
||||||
model.len() - 1
|
|
||||||
} else {
|
|
||||||
model_clip.bot + 1
|
|
||||||
};
|
|
||||||
|
|
||||||
ModelClipIterator {
|
ModelClipIterator {
|
||||||
model_idx: model_clip_top,
|
model_idx: model_clip_top,
|
||||||
|
523
src/shell.rs
523
src/shell.rs
@ -1,8 +1,10 @@
|
|||||||
|
use std;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::{Arc, Condvar, Mutex};
|
use std::sync::{Arc, Condvar, Mutex};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use cairo;
|
use cairo;
|
||||||
@ -19,21 +21,24 @@ use pangocairo;
|
|||||||
use neovim_lib::{Neovim, NeovimApi, NeovimApiAsync, Value};
|
use neovim_lib::{Neovim, NeovimApi, NeovimApiAsync, Value};
|
||||||
use neovim_lib::neovim_api::Tabpage;
|
use neovim_lib::neovim_api::Tabpage;
|
||||||
|
|
||||||
|
use misc::{decode_uri, escape_filename};
|
||||||
use settings::{FontSource, Settings};
|
use settings::{FontSource, Settings};
|
||||||
use ui_model::{Attrs, ModelRect, UiModel};
|
use ui_model::{Attrs, ModelRect, UiModel};
|
||||||
use color::{Color, ColorModel, COLOR_BLACK, COLOR_RED, COLOR_WHITE};
|
use color::{Color, ColorModel, COLOR_BLACK, COLOR_RED, COLOR_WHITE};
|
||||||
use nvim::{self, CompleteItem, ErrorReport, GuiApi, NeovimClient, NeovimClientAsync, NeovimRef,
|
use nvim::{self, CompleteItem, ErrorReport, NeovimClient, NeovimClientAsync, NeovimRef,
|
||||||
RedrawEvents, RepaintMode};
|
NvimHandler, RepaintMode};
|
||||||
use input;
|
use input;
|
||||||
use input::keyval_to_input_string;
|
use input::keyval_to_input_string;
|
||||||
use cursor::Cursor;
|
use cursor::{BlinkCursor, Cursor, CursorRedrawCb};
|
||||||
use ui::UiMutex;
|
use ui::UiMutex;
|
||||||
use popup_menu::PopupMenu;
|
use popup_menu::{self, PopupMenu};
|
||||||
use tabline::Tabline;
|
use tabline::Tabline;
|
||||||
|
use cmd_line::{CmdLine, CmdLineContext};
|
||||||
use error;
|
use error;
|
||||||
use mode;
|
use mode;
|
||||||
use render;
|
use render;
|
||||||
use render::CellMetrics;
|
use render::CellMetrics;
|
||||||
|
use subscriptions::{SubscriptionHandle, Subscriptions};
|
||||||
|
|
||||||
const DEFAULT_FONT_NAME: &str = "DejaVu Sans Mono 12";
|
const DEFAULT_FONT_NAME: &str = "DejaVu Sans Mono 12";
|
||||||
pub const MINIMUM_SUPPORTED_NVIM_VERSION: &str = "0.2.2";
|
pub const MINIMUM_SUPPORTED_NVIM_VERSION: &str = "0.2.2";
|
||||||
@ -78,18 +83,35 @@ impl Surface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RenderState {
|
||||||
|
pub font_ctx: render::Context,
|
||||||
|
pub color_model: ColorModel,
|
||||||
|
pub mode: mode::Mode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderState {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
RenderState {
|
||||||
|
font_ctx: render::Context::new(FontDescription::from_string(DEFAULT_FONT_NAME)),
|
||||||
|
color_model: ColorModel::new(),
|
||||||
|
mode: mode::Mode::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub model: UiModel,
|
pub model: UiModel,
|
||||||
pub color_model: ColorModel,
|
|
||||||
cur_attrs: Option<Attrs>,
|
cur_attrs: Option<Attrs>,
|
||||||
mouse_enabled: bool,
|
mouse_enabled: bool,
|
||||||
nvim: Rc<NeovimClient>,
|
nvim: Rc<NeovimClient>,
|
||||||
pub font_ctx: render::Context,
|
cursor: Option<BlinkCursor<State>>,
|
||||||
cursor: Option<Cursor>,
|
popup_menu: PopupMenu,
|
||||||
popup_menu: RefCell<PopupMenu>,
|
cmd_line: CmdLine,
|
||||||
settings: Rc<RefCell<Settings>>,
|
settings: Rc<RefCell<Settings>>,
|
||||||
|
render_state: Rc<RefCell<RenderState>>,
|
||||||
|
|
||||||
surface: Option<Surface>,
|
surface: Option<Surface>,
|
||||||
|
enable_double_buffer: bool,
|
||||||
|
|
||||||
resize_request: (i64, i64),
|
resize_request: (i64, i64),
|
||||||
resize_timer: Rc<Cell<Option<glib::SourceId>>>,
|
resize_timer: Rc<Cell<Option<glib::SourceId>>>,
|
||||||
@ -109,26 +131,33 @@ pub struct State {
|
|||||||
|
|
||||||
detach_cb: Option<Box<RefCell<FnMut() + Send + 'static>>>,
|
detach_cb: Option<Box<RefCell<FnMut() + Send + 'static>>>,
|
||||||
nvim_started_cb: Option<Box<RefCell<FnMut() + Send + 'static>>>,
|
nvim_started_cb: Option<Box<RefCell<FnMut() + Send + 'static>>>,
|
||||||
|
command_cb: Option<Box<FnMut(Vec<Value>) + Send + 'static>>,
|
||||||
|
|
||||||
|
subscriptions: RefCell<Subscriptions>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub fn new(settings: Rc<RefCell<Settings>>, options: ShellOptions) -> State {
|
pub fn new(settings: Rc<RefCell<Settings>>, options: ShellOptions) -> State {
|
||||||
let drawing_area = gtk::DrawingArea::new();
|
let drawing_area = gtk::DrawingArea::new();
|
||||||
let popup_menu = RefCell::new(PopupMenu::new(&drawing_area));
|
let render_state = Rc::new(RefCell::new(RenderState::new()));
|
||||||
let font_ctx = render::Context::new(FontDescription::from_string(DEFAULT_FONT_NAME));
|
let popup_menu = PopupMenu::new(&drawing_area);
|
||||||
|
let cmd_line = CmdLine::new(&drawing_area, render_state.clone());
|
||||||
|
|
||||||
State {
|
State {
|
||||||
model: UiModel::empty(),
|
model: UiModel::empty(),
|
||||||
color_model: ColorModel::new(),
|
|
||||||
nvim: Rc::new(NeovimClient::new()),
|
nvim: Rc::new(NeovimClient::new()),
|
||||||
cur_attrs: None,
|
cur_attrs: None,
|
||||||
mouse_enabled: true,
|
mouse_enabled: true,
|
||||||
font_ctx,
|
|
||||||
cursor: None,
|
cursor: None,
|
||||||
popup_menu,
|
popup_menu,
|
||||||
|
cmd_line,
|
||||||
settings,
|
settings,
|
||||||
|
render_state,
|
||||||
|
|
||||||
surface: None,
|
surface: None,
|
||||||
|
enable_double_buffer: std::env::var("NVIM_GTK_DOUBLE_BUFFER")
|
||||||
|
.map(|opt| opt.trim() == "1")
|
||||||
|
.unwrap_or(false),
|
||||||
|
|
||||||
resize_request: (-1, -1),
|
resize_request: (-1, -1),
|
||||||
resize_timer: Rc::new(Cell::new(None)),
|
resize_timer: Rc::new(Cell::new(None)),
|
||||||
@ -149,10 +178,17 @@ impl State {
|
|||||||
|
|
||||||
detach_cb: None,
|
detach_cb: None,
|
||||||
nvim_started_cb: None,
|
nvim_started_cb: None,
|
||||||
|
command_cb: None,
|
||||||
|
|
||||||
|
subscriptions: RefCell::new(Subscriptions::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resize_surface(&mut self) {
|
fn resize_surface(&mut self) {
|
||||||
|
if !self.enable_double_buffer {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(Surface { width, height, .. }) = self.surface {
|
if let Some(Surface { width, height, .. }) = self.surface {
|
||||||
let alloc = self.drawing_area.get_allocation();
|
let alloc = self.drawing_area.get_allocation();
|
||||||
|
|
||||||
@ -211,12 +247,22 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_font_desc(&self) -> &FontDescription {
|
pub fn set_nvim_command_cb<F>(&mut self, cb: Option<F>)
|
||||||
self.font_ctx.font_description()
|
where
|
||||||
|
F: FnMut(Vec<Value>) + Send + 'static,
|
||||||
|
{
|
||||||
|
if cb.is_some() {
|
||||||
|
self.command_cb = Some(Box::new(cb.unwrap()));
|
||||||
|
} else {
|
||||||
|
self.command_cb = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_font_desc(&mut self, desc: &str) {
|
pub fn set_font_desc(&mut self, desc: &str) {
|
||||||
self.font_ctx.update(FontDescription::from_string(desc));
|
self.render_state
|
||||||
|
.borrow_mut()
|
||||||
|
.font_ctx
|
||||||
|
.update(FontDescription::from_string(desc));
|
||||||
self.model.clear_glyphs();
|
self.model.clear_glyphs();
|
||||||
self.try_nvim_resize();
|
self.try_nvim_resize();
|
||||||
self.on_redraw(&RepaintMode::All);
|
self.on_redraw(&RepaintMode::All);
|
||||||
@ -224,13 +270,17 @@ impl State {
|
|||||||
|
|
||||||
pub fn open_file(&self, path: &str) {
|
pub fn open_file(&self, path: &str) {
|
||||||
if let Some(mut nvim) = self.nvim() {
|
if let Some(mut nvim) = self.nvim() {
|
||||||
nvim.command(&format!("e {}", path)).report_err();
|
nvim.command_async(&format!("e {}", path))
|
||||||
|
.cb(|r| r.report_err())
|
||||||
|
.call();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cd(&self, path: &str) {
|
pub fn cd(&self, path: &str) {
|
||||||
if let Some(mut nvim) = self.nvim() {
|
if let Some(mut nvim) = self.nvim() {
|
||||||
nvim.command(&format!("cd {}", path)).report_err();
|
nvim.command_async(&format!("cd {}", path))
|
||||||
|
.cb(|r| r.report_err())
|
||||||
|
.call();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,7 +293,7 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn close_popup_menu(&self) {
|
fn close_popup_menu(&self) {
|
||||||
if self.popup_menu.borrow().is_open() {
|
if self.popup_menu.is_open() {
|
||||||
if let Some(mut nvim) = self.nvim() {
|
if let Some(mut nvim) = self.nvim() {
|
||||||
nvim.input("<Esc>").report_err();
|
nvim.input("<Esc>").report_err();
|
||||||
}
|
}
|
||||||
@ -264,18 +314,25 @@ impl State {
|
|||||||
|
|
||||||
self.update_dirty_glyphs();
|
self.update_dirty_glyphs();
|
||||||
|
|
||||||
|
let render_state = self.render_state.borrow();
|
||||||
|
let cell_metrics = render_state.font_ctx.cell_metrics();
|
||||||
|
|
||||||
for mut rect in rects {
|
for mut rect in rects {
|
||||||
rect.extend_by_items(&self.model);
|
rect.extend_by_items(&self.model);
|
||||||
|
|
||||||
let (x, y, width, height) =
|
let (x, y, width, height) = rect.to_area_extend_ink(&self.model, cell_metrics);
|
||||||
rect.to_area_extend_ink(&self.model, self.font_ctx.cell_metrics());
|
|
||||||
self.drawing_area.queue_draw_area(x, y, width, height);
|
self.drawing_area.queue_draw_area(x, y, width, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn update_dirty_glyphs(&mut self) {
|
fn update_dirty_glyphs(&mut self) {
|
||||||
render::shape_dirty(&self.font_ctx, &mut self.model, &self.color_model);
|
let render_state = self.render_state.borrow();
|
||||||
|
render::shape_dirty(
|
||||||
|
&render_state.font_ctx,
|
||||||
|
&mut self.model,
|
||||||
|
&render_state.color_model,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn im_commit(&self, ch: &str) {
|
fn im_commit(&self, ch: &str) {
|
||||||
@ -289,7 +346,7 @@ impl State {
|
|||||||
line_height,
|
line_height,
|
||||||
char_width,
|
char_width,
|
||||||
..
|
..
|
||||||
} = self.font_ctx.cell_metrics();
|
} = self.render_state.borrow().font_ctx.cell_metrics();
|
||||||
let alloc = self.drawing_area.get_allocation();
|
let alloc = self.drawing_area.get_allocation();
|
||||||
(
|
(
|
||||||
(alloc.width as f64 / char_width).trunc() as usize,
|
(alloc.width as f64 / char_width).trunc() as usize,
|
||||||
@ -309,7 +366,7 @@ impl State {
|
|||||||
let (row, col) = self.model.get_cursor();
|
let (row, col) = self.model.get_cursor();
|
||||||
|
|
||||||
let (x, y, width, height) =
|
let (x, y, width, height) =
|
||||||
ModelRect::point(col, row).to_area(self.font_ctx.cell_metrics());
|
ModelRect::point(col, row).to_area(self.render_state.borrow().font_ctx.cell_metrics());
|
||||||
|
|
||||||
self.im_context.set_cursor_location(&gdk::Rectangle {
|
self.im_context.set_cursor_location(&gdk::Rectangle {
|
||||||
x,
|
x,
|
||||||
@ -362,7 +419,10 @@ impl State {
|
|||||||
fn edit_paste(&self, clipboard: &str) {
|
fn edit_paste(&self, clipboard: &str) {
|
||||||
let nvim = self.nvim();
|
let nvim = self.nvim();
|
||||||
if let Some(mut nvim) = nvim {
|
if let Some(mut nvim) = nvim {
|
||||||
if self.mode.is(&mode::NvimMode::Insert) || self.mode.is(&mode::NvimMode::Normal) {
|
let render_state = self.render_state.borrow();
|
||||||
|
if render_state.mode.is(&mode::NvimMode::Insert)
|
||||||
|
|| render_state.mode.is(&mode::NvimMode::Normal)
|
||||||
|
{
|
||||||
let paste_code = format!("normal! \"{}P", clipboard);
|
let paste_code = format!("normal! \"{}P", clipboard);
|
||||||
nvim.command_async(&paste_code)
|
nvim.command_async(&paste_code)
|
||||||
.cb(|r| r.report_err())
|
.cb(|r| r.report_err())
|
||||||
@ -373,6 +433,50 @@ impl State {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn max_popup_width(&self) -> i32 {
|
||||||
|
self.drawing_area.get_allocated_width() - 20
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subscribe<F>(&self, event_name: &str, args: &[&str], cb: F) -> SubscriptionHandle
|
||||||
|
where
|
||||||
|
F: Fn(Vec<String>) + 'static,
|
||||||
|
{
|
||||||
|
self.subscriptions
|
||||||
|
.borrow_mut()
|
||||||
|
.subscribe(event_name, args, cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_autocmds(&self) {
|
||||||
|
self.subscriptions
|
||||||
|
.borrow()
|
||||||
|
.set_autocmds(&mut self.nvim().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn notify(&self, params: Vec<Value>) -> Result<(), String> {
|
||||||
|
self.subscriptions.borrow().notify(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_now(&self, handle: &SubscriptionHandle) {
|
||||||
|
self.subscriptions
|
||||||
|
.borrow()
|
||||||
|
.run_now(handle, &mut self.nvim().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_font(&mut self, font_desc: String) {
|
||||||
|
{
|
||||||
|
let mut settings = self.settings.borrow_mut();
|
||||||
|
settings.set_font_source(FontSource::Rpc);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_font_desc(&font_desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_command(&mut self, args: Vec<Value>) {
|
||||||
|
if let Some(ref mut cb) = self.command_cb {
|
||||||
|
cb(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UiState {
|
pub struct UiState {
|
||||||
@ -392,19 +496,19 @@ impl UiState {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ShellOptions {
|
pub struct ShellOptions {
|
||||||
nvim_bin_path: Option<String>,
|
nvim_bin_path: Option<String>,
|
||||||
open_path: Option<String>,
|
open_paths: Vec<String>,
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShellOptions {
|
impl ShellOptions {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
nvim_bin_path: Option<String>,
|
nvim_bin_path: Option<String>,
|
||||||
open_path: Option<String>,
|
open_paths: Vec<String>,
|
||||||
timeout: Option<Duration>,
|
timeout: Option<Duration>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ShellOptions {
|
ShellOptions {
|
||||||
nvim_bin_path,
|
nvim_bin_path,
|
||||||
open_path,
|
open_paths,
|
||||||
timeout,
|
timeout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -427,7 +531,7 @@ impl Shell {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let shell_ref = Arc::downgrade(&shell.state);
|
let shell_ref = Arc::downgrade(&shell.state);
|
||||||
shell.state.borrow_mut().cursor = Some(Cursor::new(shell_ref));
|
shell.state.borrow_mut().cursor = Some(BlinkCursor::new(shell_ref));
|
||||||
|
|
||||||
shell
|
shell
|
||||||
}
|
}
|
||||||
@ -582,6 +686,29 @@ impl Shell {
|
|||||||
state.drawing_area.connect_size_allocate(move |_, _| {
|
state.drawing_area.connect_size_allocate(move |_, _| {
|
||||||
init_nvim(&ref_state);
|
init_nvim(&ref_state);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let ref_state = self.state.clone();
|
||||||
|
let targets = vec![
|
||||||
|
gtk::TargetEntry::new("text/uri-list", gtk::TargetFlags::OTHER_APP, 0),
|
||||||
|
];
|
||||||
|
state
|
||||||
|
.drawing_area
|
||||||
|
.drag_dest_set(gtk::DestDefaults::ALL, &targets, gdk::DragAction::COPY);
|
||||||
|
state
|
||||||
|
.drawing_area
|
||||||
|
.connect_drag_data_received(move |_, _, _, _, s, _, _| {
|
||||||
|
let uris = s.get_uris();
|
||||||
|
let command = uris.iter().filter_map(|uri| decode_uri(uri)).fold(
|
||||||
|
":ar".to_owned(),
|
||||||
|
|command, filename| {
|
||||||
|
let filename = escape_filename(&filename);
|
||||||
|
command + " " + &filename
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let state = ref_state.borrow_mut();
|
||||||
|
let mut nvim = state.nvim().unwrap();
|
||||||
|
nvim.command_async(&command).cb(|r| r.report_err()).call()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@ -619,7 +746,16 @@ impl Shell {
|
|||||||
|
|
||||||
let nvim = state.nvim();
|
let nvim = state.nvim();
|
||||||
if let Some(mut nvim) = nvim {
|
if let Some(mut nvim) = nvim {
|
||||||
nvim.command(":wa").report_err();
|
nvim.command_async(":wa").cb(|r| r.report_err()).call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_tab(&self) {
|
||||||
|
let state = self.state.borrow();
|
||||||
|
|
||||||
|
let nvim = state.nvim();
|
||||||
|
if let Some(mut nvim) = nvim {
|
||||||
|
nvim.command_async(":tabe").cb(|r| r.report_err()).call();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,6 +774,14 @@ impl Shell {
|
|||||||
let mut state = self.state.borrow_mut();
|
let mut state = self.state.borrow_mut();
|
||||||
state.set_nvim_started_cb(cb);
|
state.set_nvim_started_cb(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_nvim_command_cb<F>(&self, cb: Option<F>)
|
||||||
|
where
|
||||||
|
F: FnMut(Vec<Value>) + Send + 'static,
|
||||||
|
{
|
||||||
|
let mut state = self.state.borrow_mut();
|
||||||
|
state.set_nvim_command_cb(cb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Shell {
|
impl Deref for Shell {
|
||||||
@ -749,7 +893,7 @@ fn mouse_input(shell: &mut State, input: &str, state: ModifierType, position: (f
|
|||||||
line_height,
|
line_height,
|
||||||
char_width,
|
char_width,
|
||||||
..
|
..
|
||||||
} = shell.font_ctx.cell_metrics();
|
} = shell.render_state.borrow().font_ctx.cell_metrics();
|
||||||
let (x, y) = position;
|
let (x, y) = position;
|
||||||
let col = (x / char_width).trunc() as u64;
|
let col = (x / char_width).trunc() as u64;
|
||||||
let row = (y / line_height).trunc() as u64;
|
let row = (y / line_height).trunc() as u64;
|
||||||
@ -784,32 +928,54 @@ fn gtk_motion_notify(shell: &mut State, ui_state: &mut UiState, ev: &EventMotion
|
|||||||
Inhibit(false)
|
Inhibit(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn gtk_draw_double_buffer(state: &State, ctx: &cairo::Context) {
|
||||||
|
let (x1, y1, x2, y2) = ctx.clip_extents();
|
||||||
|
let surface = state.surface.as_ref().unwrap();
|
||||||
|
let buf_ctx = &surface.ctx;
|
||||||
|
|
||||||
|
surface.surface.flush();
|
||||||
|
|
||||||
|
buf_ctx.save();
|
||||||
|
buf_ctx.rectangle(x1, y1, x2 - x1, y2 - y1);
|
||||||
|
buf_ctx.clip();
|
||||||
|
|
||||||
|
let render_state = state.render_state.borrow();
|
||||||
|
render::clear(buf_ctx, &render_state.color_model);
|
||||||
|
render::render(
|
||||||
|
&buf_ctx,
|
||||||
|
state.cursor.as_ref().unwrap(),
|
||||||
|
&render_state.font_ctx,
|
||||||
|
&state.model,
|
||||||
|
&render_state.color_model,
|
||||||
|
&render_state.mode,
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.set_source_surface(&surface.surface, 0.0, 0.0);
|
||||||
|
ctx.paint();
|
||||||
|
buf_ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gtk_draw_direct(state: &State, ctx: &cairo::Context) {
|
||||||
|
let render_state = state.render_state.borrow();
|
||||||
|
render::clear(ctx, &render_state.color_model);
|
||||||
|
render::render(
|
||||||
|
ctx,
|
||||||
|
state.cursor.as_ref().unwrap(),
|
||||||
|
&render_state.font_ctx,
|
||||||
|
&state.model,
|
||||||
|
&render_state.color_model,
|
||||||
|
&render_state.mode,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn gtk_draw(state_arc: &Arc<UiMutex<State>>, ctx: &cairo::Context) -> Inhibit {
|
fn gtk_draw(state_arc: &Arc<UiMutex<State>>, ctx: &cairo::Context) -> Inhibit {
|
||||||
let state = state_arc.borrow();
|
let state = state_arc.borrow();
|
||||||
if state.nvim.is_initialized() {
|
if state.nvim.is_initialized() {
|
||||||
|
if state.enable_double_buffer {
|
||||||
let (x1, y1, x2, y2) = ctx.clip_extents();
|
gtk_draw_double_buffer(&*state, ctx);
|
||||||
let surface = state.surface.as_ref().unwrap();
|
} else {
|
||||||
let buf_ctx = &surface.ctx;
|
gtk_draw_direct(&*state, ctx);
|
||||||
|
}
|
||||||
surface.surface.flush();
|
|
||||||
|
|
||||||
buf_ctx.save();
|
|
||||||
buf_ctx.rectangle(x1, y1, x2 - x1, y2 - y1);
|
|
||||||
buf_ctx.clip();
|
|
||||||
|
|
||||||
render::render(
|
|
||||||
&buf_ctx,
|
|
||||||
state.cursor.as_ref().unwrap(),
|
|
||||||
&state.font_ctx,
|
|
||||||
&state.model,
|
|
||||||
&state.color_model,
|
|
||||||
&state.mode,
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.set_source_surface(&surface.surface, 0.0, 0.0);
|
|
||||||
ctx.paint();
|
|
||||||
buf_ctx.restore();
|
|
||||||
} else if state.nvim.is_initializing() {
|
} else if state.nvim.is_initializing() {
|
||||||
draw_initializing(&*state, ctx);
|
draw_initializing(&*state, ctx);
|
||||||
}
|
}
|
||||||
@ -846,13 +1012,14 @@ fn show_nvim_init_error(err: &nvim::NvimInitError, state_arc: Arc<UiMutex<State>
|
|||||||
|
|
||||||
fn init_nvim_async(
|
fn init_nvim_async(
|
||||||
state_arc: Arc<UiMutex<State>>,
|
state_arc: Arc<UiMutex<State>>,
|
||||||
|
nvim_handler: NvimHandler,
|
||||||
options: ShellOptions,
|
options: ShellOptions,
|
||||||
cols: usize,
|
cols: usize,
|
||||||
rows: usize,
|
rows: usize,
|
||||||
) {
|
) {
|
||||||
// execute nvim
|
// execute nvim
|
||||||
let nvim = match nvim::start(
|
let nvim = match nvim::start(
|
||||||
state_arc.clone(),
|
nvim_handler,
|
||||||
options.nvim_bin_path.as_ref(),
|
options.nvim_bin_path.as_ref(),
|
||||||
options.timeout,
|
options.timeout,
|
||||||
) {
|
) {
|
||||||
@ -882,9 +1049,7 @@ fn init_nvim_async(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// attach ui
|
// attach ui
|
||||||
if let Err(err) =
|
if let Err(err) = nvim::post_start_init(nvim, options.open_paths, cols as u64, rows as u64) {
|
||||||
nvim::post_start_init(nvim, options.open_path.as_ref(), cols as u64, rows as u64)
|
|
||||||
{
|
|
||||||
show_nvim_init_error(&err, state_arc.clone());
|
show_nvim_init_error(&err, state_arc.clone());
|
||||||
} else {
|
} else {
|
||||||
set_nvim_initialized(state_arc);
|
set_nvim_initialized(state_arc);
|
||||||
@ -934,13 +1099,15 @@ fn set_nvim_initialized(state_arc: Arc<UiMutex<State>>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn draw_initializing(state: &State, ctx: &cairo::Context) {
|
fn draw_initializing(state: &State, ctx: &cairo::Context) {
|
||||||
|
let render_state = state.render_state.borrow();
|
||||||
|
let color_model = &render_state.color_model;
|
||||||
|
let layout = pangocairo::functions::create_layout(ctx).unwrap();
|
||||||
let alloc = state.drawing_area.get_allocation();
|
let alloc = state.drawing_area.get_allocation();
|
||||||
let layout = state.font_ctx.create_layout();
|
|
||||||
|
|
||||||
ctx.set_source_rgb(
|
ctx.set_source_rgb(
|
||||||
state.color_model.bg_color.0,
|
color_model.bg_color.0,
|
||||||
state.color_model.bg_color.1,
|
color_model.bg_color.1,
|
||||||
state.color_model.bg_color.2,
|
color_model.bg_color.2,
|
||||||
);
|
);
|
||||||
ctx.paint();
|
ctx.paint();
|
||||||
|
|
||||||
@ -952,9 +1119,9 @@ fn draw_initializing(state: &State, ctx: &cairo::Context) {
|
|||||||
|
|
||||||
ctx.move_to(x, y);
|
ctx.move_to(x, y);
|
||||||
ctx.set_source_rgb(
|
ctx.set_source_rgb(
|
||||||
state.color_model.fg_color.0,
|
color_model.fg_color.0,
|
||||||
state.color_model.fg_color.1,
|
color_model.fg_color.1,
|
||||||
state.color_model.fg_color.2,
|
color_model.fg_color.2,
|
||||||
);
|
);
|
||||||
pangocairo::functions::update_layout(ctx, &layout);
|
pangocairo::functions::update_layout(ctx, &layout);
|
||||||
pangocairo::functions::show_layout(ctx, &layout);
|
pangocairo::functions::show_layout(ctx, &layout);
|
||||||
@ -962,11 +1129,11 @@ fn draw_initializing(state: &State, ctx: &cairo::Context) {
|
|||||||
ctx.move_to(x + width as f64, y);
|
ctx.move_to(x + width as f64, y);
|
||||||
state.cursor.as_ref().unwrap().draw(
|
state.cursor.as_ref().unwrap().draw(
|
||||||
ctx,
|
ctx,
|
||||||
&state.font_ctx,
|
&render_state.font_ctx,
|
||||||
&state.mode,
|
&render_state.mode,
|
||||||
y,
|
y,
|
||||||
false,
|
false,
|
||||||
&state.color_model.bg_color,
|
&color_model.bg_color,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -980,32 +1147,38 @@ fn init_nvim(state_ref: &Arc<UiMutex<State>>) {
|
|||||||
state.model = UiModel::new(rows as u64, cols as u64);
|
state.model = UiModel::new(rows as u64, cols as u64);
|
||||||
|
|
||||||
let state_arc = state_ref.clone();
|
let state_arc = state_ref.clone();
|
||||||
|
let nvim_handler = NvimHandler::new(state_ref.clone());
|
||||||
let options = state.options.clone();
|
let options = state.options.clone();
|
||||||
thread::spawn(move || init_nvim_async(state_arc, options, cols, rows));
|
thread::spawn(move || init_nvim_async(state_arc, nvim_handler, options, cols, rows));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RedrawEvents for State {
|
// Neovim redraw events
|
||||||
fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode {
|
impl State {
|
||||||
|
pub fn on_cursor_goto(&mut self, row: u64, col: u64) -> RepaintMode {
|
||||||
let repaint_area = self.model.set_cursor(row as usize, col as usize);
|
let repaint_area = self.model.set_cursor(row as usize, col as usize);
|
||||||
self.set_im_location();
|
self.set_im_location();
|
||||||
RepaintMode::AreaList(repaint_area)
|
RepaintMode::AreaList(repaint_area)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_put(&mut self, text: &str) -> RepaintMode {
|
pub fn on_put(&mut self, text: String) -> RepaintMode {
|
||||||
RepaintMode::Area(self.model.put(text, self.cur_attrs.as_ref()))
|
let ch = text.chars().last().unwrap_or(' ');
|
||||||
|
let double_width = text.is_empty();
|
||||||
|
RepaintMode::Area(self.model.put(ch, double_width, self.cur_attrs.as_ref()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_clear(&mut self) -> RepaintMode {
|
pub fn on_clear(&mut self) -> RepaintMode {
|
||||||
|
debug!("clear model");
|
||||||
|
|
||||||
self.model.clear();
|
self.model.clear();
|
||||||
RepaintMode::All
|
RepaintMode::All
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_eol_clear(&mut self) -> RepaintMode {
|
pub fn on_eol_clear(&mut self) -> RepaintMode {
|
||||||
RepaintMode::Area(self.model.eol_clear())
|
RepaintMode::Area(self.model.eol_clear())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_resize(&mut self, columns: u64, rows: u64) -> RepaintMode {
|
pub fn on_resize(&mut self, columns: u64, rows: u64) -> RepaintMode {
|
||||||
debug!("on_resize {}/{}", columns, rows);
|
debug!("on_resize {}/{}", columns, rows);
|
||||||
|
|
||||||
if self.model.columns != columns as usize || self.model.rows != rows as usize {
|
if self.model.columns != columns as usize || self.model.rows != rows as usize {
|
||||||
@ -1013,12 +1186,13 @@ impl RedrawEvents for State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(mut nvim) = self.nvim.nvim() {
|
if let Some(mut nvim) = self.nvim.nvim() {
|
||||||
self.color_model.theme.update(&mut *nvim);
|
let mut render_state = self.render_state.borrow_mut();
|
||||||
|
render_state.color_model.theme.update(&mut *nvim);
|
||||||
}
|
}
|
||||||
RepaintMode::Nothing
|
RepaintMode::Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_redraw(&mut self, mode: &RepaintMode) {
|
pub fn on_redraw(&mut self, mode: &RepaintMode) {
|
||||||
match *mode {
|
match *mode {
|
||||||
RepaintMode::All => {
|
RepaintMode::All => {
|
||||||
self.update_dirty_glyphs();
|
self.update_dirty_glyphs();
|
||||||
@ -1030,90 +1204,70 @@ impl RedrawEvents for State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_set_scroll_region(&mut self, top: u64, bot: u64, left: u64, right: u64) -> RepaintMode {
|
pub fn on_set_scroll_region(
|
||||||
|
&mut self,
|
||||||
|
top: u64,
|
||||||
|
bot: u64,
|
||||||
|
left: u64,
|
||||||
|
right: u64,
|
||||||
|
) -> RepaintMode {
|
||||||
self.model.set_scroll_region(top, bot, left, right);
|
self.model.set_scroll_region(top, bot, left, right);
|
||||||
RepaintMode::Nothing
|
RepaintMode::Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_scroll(&mut self, count: i64) -> RepaintMode {
|
pub fn on_scroll(&mut self, count: i64) -> RepaintMode {
|
||||||
RepaintMode::Area(self.model.scroll(count))
|
RepaintMode::Area(self.model.scroll(count))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_highlight_set(&mut self, attrs: &[(Value, Value)]) -> RepaintMode {
|
pub fn on_highlight_set(&mut self, attrs: HashMap<String, Value>) -> RepaintMode {
|
||||||
let mut model_attrs = Attrs::new();
|
let model_attrs = Attrs::from_value_map(&attrs);
|
||||||
|
|
||||||
for &(ref key_val, ref val) in attrs {
|
|
||||||
if let Some(key) = key_val.as_str() {
|
|
||||||
match key {
|
|
||||||
"foreground" => {
|
|
||||||
if let Some(fg) = val.as_u64() {
|
|
||||||
model_attrs.foreground = Some(Color::from_indexed_color(fg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"background" => {
|
|
||||||
if let Some(bg) = val.as_u64() {
|
|
||||||
model_attrs.background = Some(Color::from_indexed_color(bg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"special" => {
|
|
||||||
if let Some(bg) = val.as_u64() {
|
|
||||||
model_attrs.special = Some(Color::from_indexed_color(bg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"reverse" => model_attrs.reverse = true,
|
|
||||||
"bold" => model_attrs.bold = true,
|
|
||||||
"italic" => model_attrs.italic = true,
|
|
||||||
"underline" => model_attrs.underline = true,
|
|
||||||
"undercurl" => model_attrs.undercurl = true,
|
|
||||||
attr_key => println!("unknown attribute {}", attr_key),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
panic!("attr key must be string");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.cur_attrs = Some(model_attrs);
|
self.cur_attrs = Some(model_attrs);
|
||||||
RepaintMode::Nothing
|
RepaintMode::Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_update_bg(&mut self, bg: i64) -> RepaintMode {
|
pub fn on_update_bg(&mut self, bg: i64) -> RepaintMode {
|
||||||
|
let mut render_state = self.render_state.borrow_mut();
|
||||||
if bg >= 0 {
|
if bg >= 0 {
|
||||||
self.color_model.bg_color = Color::from_indexed_color(bg as u64);
|
render_state.color_model.bg_color = Color::from_indexed_color(bg as u64);
|
||||||
} else {
|
} else {
|
||||||
self.color_model.bg_color = COLOR_BLACK;
|
render_state.color_model.bg_color = COLOR_BLACK;
|
||||||
}
|
}
|
||||||
RepaintMode::Nothing
|
RepaintMode::Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_update_fg(&mut self, fg: i64) -> RepaintMode {
|
pub fn on_update_fg(&mut self, fg: i64) -> RepaintMode {
|
||||||
|
let mut render_state = self.render_state.borrow_mut();
|
||||||
if fg >= 0 {
|
if fg >= 0 {
|
||||||
self.color_model.fg_color = Color::from_indexed_color(fg as u64);
|
render_state.color_model.fg_color = Color::from_indexed_color(fg as u64);
|
||||||
} else {
|
} else {
|
||||||
self.color_model.fg_color = COLOR_WHITE;
|
render_state.color_model.fg_color = COLOR_WHITE;
|
||||||
}
|
}
|
||||||
RepaintMode::Nothing
|
RepaintMode::Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_update_sp(&mut self, sp: i64) -> RepaintMode {
|
pub fn on_update_sp(&mut self, sp: i64) -> RepaintMode {
|
||||||
|
let mut render_state = self.render_state.borrow_mut();
|
||||||
if sp >= 0 {
|
if sp >= 0 {
|
||||||
self.color_model.sp_color = Color::from_indexed_color(sp as u64);
|
render_state.color_model.sp_color = Color::from_indexed_color(sp as u64);
|
||||||
} else {
|
} else {
|
||||||
self.color_model.sp_color = COLOR_RED;
|
render_state.color_model.sp_color = COLOR_RED;
|
||||||
}
|
}
|
||||||
RepaintMode::Nothing
|
RepaintMode::Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_mode_change(&mut self, mode: &str, idx: u64) -> RepaintMode {
|
pub fn on_mode_change(&mut self, mode: String, idx: u64) -> RepaintMode {
|
||||||
self.mode.update(mode, idx as usize);
|
let mut render_state = self.render_state.borrow_mut();
|
||||||
|
render_state.mode.update(&mode, idx as usize);
|
||||||
RepaintMode::Area(self.model.cur_point())
|
RepaintMode::Area(self.model.cur_point())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_mouse(&mut self, on: bool) -> RepaintMode {
|
pub fn on_mouse(&mut self, on: bool) -> RepaintMode {
|
||||||
self.mouse_enabled = on;
|
self.mouse_enabled = on;
|
||||||
RepaintMode::Nothing
|
RepaintMode::Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_busy(&mut self, busy: bool) -> RepaintMode {
|
pub fn on_busy(&mut self, busy: bool) -> RepaintMode {
|
||||||
if busy {
|
if busy {
|
||||||
self.cursor.as_mut().unwrap().busy_on();
|
self.cursor.as_mut().unwrap().busy_on();
|
||||||
} else {
|
} else {
|
||||||
@ -1122,7 +1276,7 @@ impl RedrawEvents for State {
|
|||||||
RepaintMode::Area(self.model.cur_point())
|
RepaintMode::Area(self.model.cur_point())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn popupmenu_show(
|
pub fn popupmenu_show(
|
||||||
&mut self,
|
&mut self,
|
||||||
menu: &[CompleteItem],
|
menu: &[CompleteItem],
|
||||||
selected: i64,
|
selected: i64,
|
||||||
@ -1130,26 +1284,38 @@ impl RedrawEvents for State {
|
|||||||
col: u64,
|
col: u64,
|
||||||
) -> RepaintMode {
|
) -> RepaintMode {
|
||||||
let point = ModelRect::point(col as usize, row as usize);
|
let point = ModelRect::point(col as usize, row as usize);
|
||||||
let (x, y, width, height) = point.to_area(self.font_ctx.cell_metrics());
|
let render_state = self.render_state.borrow();
|
||||||
|
let (x, y, width, height) = point.to_area(render_state.font_ctx.cell_metrics());
|
||||||
|
|
||||||
self.popup_menu
|
let context = popup_menu::PopupMenuContext {
|
||||||
.borrow_mut()
|
nvim: &self.nvim,
|
||||||
.show(self, menu, selected, x, y, width, height);
|
color_model: &render_state.color_model,
|
||||||
|
font_ctx: &render_state.font_ctx,
|
||||||
|
menu_items: &menu,
|
||||||
|
selected,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
max_width: self.max_popup_width(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.popup_menu.show(context);
|
||||||
|
|
||||||
RepaintMode::Nothing
|
RepaintMode::Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
fn popupmenu_hide(&mut self) -> RepaintMode {
|
pub fn popupmenu_hide(&mut self) -> RepaintMode {
|
||||||
self.popup_menu.borrow_mut().hide();
|
self.popup_menu.hide();
|
||||||
RepaintMode::Nothing
|
RepaintMode::Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
fn popupmenu_select(&mut self, selected: i64) -> RepaintMode {
|
pub fn popupmenu_select(&mut self, selected: i64) -> RepaintMode {
|
||||||
self.popup_menu.borrow().select(selected);
|
self.popup_menu.select(selected);
|
||||||
RepaintMode::Nothing
|
RepaintMode::Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tabline_update(
|
pub fn tabline_update(
|
||||||
&mut self,
|
&mut self,
|
||||||
selected: Tabpage,
|
selected: Tabpage,
|
||||||
tabs: Vec<(Tabpage, Option<String>)>,
|
tabs: Vec<(Tabpage, Option<String>)>,
|
||||||
@ -1159,23 +1325,92 @@ impl RedrawEvents for State {
|
|||||||
RepaintMode::Nothing
|
RepaintMode::Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mode_info_set(
|
pub fn mode_info_set(
|
||||||
&mut self,
|
&mut self,
|
||||||
cursor_style_enabled: bool,
|
cursor_style_enabled: bool,
|
||||||
mode_info: Vec<nvim::ModeInfo>,
|
mode_info: Vec<nvim::ModeInfo>,
|
||||||
) -> RepaintMode {
|
) -> RepaintMode {
|
||||||
self.mode.set_info(cursor_style_enabled, mode_info);
|
let mut render_state = self.render_state.borrow_mut();
|
||||||
|
render_state.mode.set_info(cursor_style_enabled, mode_info);
|
||||||
|
RepaintMode::Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmdline_show(
|
||||||
|
&mut self,
|
||||||
|
content: Vec<(HashMap<String, Value>, String)>,
|
||||||
|
pos: u64,
|
||||||
|
firstc: String,
|
||||||
|
prompt: String,
|
||||||
|
indent: u64,
|
||||||
|
level: u64,
|
||||||
|
) -> RepaintMode {
|
||||||
|
{
|
||||||
|
let cursor = self.model.cur_point();
|
||||||
|
let render_state = self.render_state.borrow();
|
||||||
|
let (x, y, width, height) = cursor.to_area(render_state.font_ctx.cell_metrics());
|
||||||
|
let ctx = CmdLineContext {
|
||||||
|
content,
|
||||||
|
pos,
|
||||||
|
firstc,
|
||||||
|
prompt,
|
||||||
|
indent,
|
||||||
|
level_idx: level,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
max_width: self.max_popup_width(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.cmd_line.show_level(&ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.on_busy(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmdline_hide(&mut self, level: u64) -> RepaintMode {
|
||||||
|
self.cmd_line.hide_level(level);
|
||||||
|
self.on_busy(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmdline_block_show(
|
||||||
|
&mut self,
|
||||||
|
content: Vec<Vec<(HashMap<String, Value>, String)>>,
|
||||||
|
) -> RepaintMode {
|
||||||
|
let max_width = self.max_popup_width();
|
||||||
|
self.cmd_line.show_block(&content, max_width);
|
||||||
|
self.on_busy(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmdline_block_append(
|
||||||
|
&mut self,
|
||||||
|
content: Vec<(HashMap<String, Value>, String)>,
|
||||||
|
) -> RepaintMode {
|
||||||
|
self.cmd_line.block_append(&content);
|
||||||
|
RepaintMode::Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmdline_block_hide(&mut self) -> RepaintMode {
|
||||||
|
self.cmd_line.block_hide();
|
||||||
|
self.on_busy(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmdline_pos(&mut self, pos: u64, level: u64) -> RepaintMode {
|
||||||
|
let render_state = self.render_state.borrow();
|
||||||
|
self.cmd_line.pos(&*render_state, pos, level);
|
||||||
|
RepaintMode::Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cmdline_special_char(&mut self, c: String, shift: bool, level: u64) -> RepaintMode {
|
||||||
|
let render_state = self.render_state.borrow();
|
||||||
|
self.cmd_line.special_char(&*render_state, c, shift, level);
|
||||||
RepaintMode::Nothing
|
RepaintMode::Nothing
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GuiApi for State {
|
impl CursorRedrawCb for State {
|
||||||
fn set_font(&mut self, font_desc: &str) {
|
fn queue_redraw_cursor(&mut self) {
|
||||||
{
|
let cur_point = self.model.cur_point();
|
||||||
let mut settings = self.settings.borrow_mut();
|
self.on_redraw(&RepaintMode::Area(cur_point));
|
||||||
settings.set_font_source(FontSource::Rpc);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.set_font_desc(font_desc);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
156
src/subscriptions.rs
Normal file
156
src/subscriptions.rs
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use neovim_lib::{NeovimApi, NeovimApiAsync, Value};
|
||||||
|
|
||||||
|
use nvim::{ErrorReport, NeovimRef};
|
||||||
|
|
||||||
|
/// A subscription to a Neovim autocmd event.
|
||||||
|
struct Subscription {
|
||||||
|
/// A callback to be executed each time the event triggers.
|
||||||
|
cb: Box<Fn(Vec<String>) + 'static>,
|
||||||
|
/// A list of expressions which will be evaluated when the event triggers. The result is passed
|
||||||
|
/// to the callback.
|
||||||
|
args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A map of all registered subscriptions.
|
||||||
|
pub struct Subscriptions(HashMap<String, Vec<Subscription>>);
|
||||||
|
|
||||||
|
/// A handle to identify a `Subscription` within the `Subscriptions` map.
|
||||||
|
///
|
||||||
|
/// Can be used to trigger the subscription manually even when the event was not triggered.
|
||||||
|
///
|
||||||
|
/// Could be used in the future to suspend individual subscriptions.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SubscriptionHandle {
|
||||||
|
event_name: String,
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Subscriptions {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Subscriptions(HashMap::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subscribe to a Neovim autocmd event.
|
||||||
|
///
|
||||||
|
/// Subscriptions are not active immediately but only after `set_autocmds` is called. At the
|
||||||
|
/// moment, all calls to `subscribe` must be made before calling `set_autocmds`.
|
||||||
|
///
|
||||||
|
/// This function is wrapped by `shell::State`.
|
||||||
|
///
|
||||||
|
/// # Arguments:
|
||||||
|
///
|
||||||
|
/// - `event_name`: The event to register.
|
||||||
|
/// See `:help autocmd-events` for a list of supported event names. Event names can be
|
||||||
|
/// comma-separated.
|
||||||
|
///
|
||||||
|
/// - `args`: A list of expressions to be evaluated when the event triggers.
|
||||||
|
/// Expressions are evaluated using Vimscript. The results are passed to the callback as a
|
||||||
|
/// list of Strings.
|
||||||
|
/// This is especially useful as `Neovim::eval` is synchronous and might block if called from
|
||||||
|
/// the callback function; so always use the `args` mechanism instead.
|
||||||
|
///
|
||||||
|
/// - `cb`: The callback function.
|
||||||
|
/// This will be called each time the event triggers or when `run_now` is called.
|
||||||
|
/// It is passed a vector with the results of the evaluated expressions given with `args`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// Call a function each time a buffer is entered or the current working directory is changed.
|
||||||
|
/// Pass the current buffer name and directory to the callback.
|
||||||
|
/// ```
|
||||||
|
/// let my_subscription = shell.state.borrow()
|
||||||
|
/// .subscribe("BufEnter,DirChanged", &["expand(@%)", "getcwd()"], move |args| {
|
||||||
|
/// let filename = &args[0];
|
||||||
|
/// let dir = &args[1];
|
||||||
|
/// // do stuff
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
|
pub fn subscribe<F>(&mut self, event_name: &str, args: &[&str], cb: F) -> SubscriptionHandle
|
||||||
|
where
|
||||||
|
F: Fn(Vec<String>) + 'static,
|
||||||
|
{
|
||||||
|
let entry = self.0.entry(event_name.to_owned()).or_insert(Vec::new());
|
||||||
|
let index = entry.len();
|
||||||
|
entry.push(Subscription {
|
||||||
|
cb: Box::new(cb),
|
||||||
|
args: args.into_iter().map(|&s| s.to_owned()).collect(),
|
||||||
|
});
|
||||||
|
SubscriptionHandle {
|
||||||
|
event_name: event_name.to_owned(),
|
||||||
|
index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register all subscriptions with Neovim.
|
||||||
|
///
|
||||||
|
/// This function is wrapped by `shell::State`.
|
||||||
|
pub fn set_autocmds(&self, nvim: &mut NeovimRef) {
|
||||||
|
for (event_name, subscriptions) in &self.0 {
|
||||||
|
for (i, subscription) in subscriptions.iter().enumerate() {
|
||||||
|
let args = subscription
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.fold("".to_owned(), |acc, arg| acc + ", " + &arg);
|
||||||
|
nvim.command_async(&format!(
|
||||||
|
"au {} * call rpcnotify(1, 'subscription', '{}', {} {})",
|
||||||
|
event_name, event_name, i, args,
|
||||||
|
)).cb(|r| r.report_err())
|
||||||
|
.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trigger given event.
|
||||||
|
fn on_notify(&self, event_name: &str, index: usize, args: Vec<String>) {
|
||||||
|
if let Some(subscription) = self.0.get(event_name).and_then(|v| v.get(index)) {
|
||||||
|
(*subscription.cb)(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper around `on_notify` for easy calling with a `neovim_lib::Handler` implementation.
|
||||||
|
///
|
||||||
|
/// This function is wrapped by `shell::State`.
|
||||||
|
pub fn notify(&self, params: Vec<Value>) -> Result<(), String> {
|
||||||
|
let mut params_iter = params.into_iter();
|
||||||
|
let ev_name = params_iter.next();
|
||||||
|
let ev_name = ev_name
|
||||||
|
.as_ref()
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.ok_or("Error reading event name")?;
|
||||||
|
let index = params_iter
|
||||||
|
.next()
|
||||||
|
.and_then(|i| i.as_u64())
|
||||||
|
.ok_or("Error reading index")? as usize;
|
||||||
|
let args = params_iter
|
||||||
|
.map(|arg| arg.as_str().map(|s| s.to_owned()))
|
||||||
|
.collect::<Option<Vec<String>>>()
|
||||||
|
.ok_or("Error reading args")?;
|
||||||
|
self.on_notify(ev_name, index, args);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manually trigger the given subscription.
|
||||||
|
///
|
||||||
|
/// The `nvim` instance is needed to evaluate the `args` expressions.
|
||||||
|
///
|
||||||
|
/// This function is wrapped by `shell::State`.
|
||||||
|
pub fn run_now(&self, handle: &SubscriptionHandle, nvim: &mut NeovimRef) {
|
||||||
|
let subscription = &self.0.get(&handle.event_name).unwrap()[handle.index];
|
||||||
|
let args = subscription
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.map(|arg| nvim.eval(arg))
|
||||||
|
.map(|res| {
|
||||||
|
res.ok()
|
||||||
|
.and_then(|val| val.as_str().map(|s: &str| s.to_owned()))
|
||||||
|
})
|
||||||
|
.collect::<Option<Vec<String>>>();
|
||||||
|
if let Some(args) = args {
|
||||||
|
self.on_notify(&handle.event_name, handle.index, args);
|
||||||
|
} else {
|
||||||
|
error!("Error manually running {:?}", handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,11 +20,6 @@ glib_wrapper! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Item {
|
impl Item {
|
||||||
#[cfg(test)]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
unsafe { from_glib_full(pango_sys::pango_item_new()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn analysis(&self) -> analysis::Analysis {
|
pub fn analysis(&self) -> analysis::Analysis {
|
||||||
analysis::Analysis::from(&self.0.analysis)
|
analysis::Analysis::from(&self.0.analysis)
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ use std::ptr;
|
|||||||
|
|
||||||
use pango;
|
use pango;
|
||||||
use pango_sys;
|
use pango_sys;
|
||||||
use glib_ffi;
|
|
||||||
|
|
||||||
use glib::translate::*;
|
use glib::translate::*;
|
||||||
|
|
||||||
@ -23,8 +22,7 @@ pub fn pango_itemize(
|
|||||||
cached_iter: Option<&mut AttrIterator>,
|
cached_iter: Option<&mut AttrIterator>,
|
||||||
) -> Vec<Item> {
|
) -> Vec<Item> {
|
||||||
unsafe {
|
unsafe {
|
||||||
//FromGlibPtrContainer::from_glib_full(pango_sys::pango_itemize(
|
FromGlibPtrContainer::from_glib_full(pango_sys::pango_itemize(
|
||||||
from_glib_full_as_vec(pango_sys::pango_itemize(
|
|
||||||
context.to_glib_none().0,
|
context.to_glib_none().0,
|
||||||
text.as_ptr() as *const i8,
|
text.as_ptr() as *const i8,
|
||||||
start_index as i32,
|
start_index as i32,
|
||||||
@ -37,12 +35,6 @@ pub fn pango_itemize(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsafe fn from_glib_full_as_vec(ptr: *mut glib_ffi::GList) -> Vec<Item> {
|
|
||||||
let num = glib_ffi::g_list_length(ptr) as usize;
|
|
||||||
FromGlibContainer::from_glib_full_num(ptr, num)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pango_shape(
|
pub fn pango_shape(
|
||||||
text: &str,
|
text: &str,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
|
@ -10,7 +10,7 @@ use glib::signal;
|
|||||||
|
|
||||||
use pango;
|
use pango;
|
||||||
|
|
||||||
use neovim_lib::NeovimApi;
|
use neovim_lib::{NeovimApi, NeovimApiAsync};
|
||||||
use neovim_lib::neovim_api::Tabpage;
|
use neovim_lib::neovim_api::Tabpage;
|
||||||
|
|
||||||
use nvim;
|
use nvim;
|
||||||
@ -39,6 +39,14 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn close_tab(&self, idx: u32) {
|
||||||
|
if let Some(mut nvim) = self.nvim.as_ref().unwrap().nvim() {
|
||||||
|
nvim.command_async(&format!(":tabc {}", idx + 1))
|
||||||
|
.cb(|r| r.report_err())
|
||||||
|
.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Tabline {
|
pub struct Tabline {
|
||||||
@ -55,6 +63,7 @@ impl Tabline {
|
|||||||
tabs.set_scrollable(true);
|
tabs.set_scrollable(true);
|
||||||
tabs.set_show_border(false);
|
tabs.set_show_border(false);
|
||||||
tabs.set_border_width(0);
|
tabs.set_border_width(0);
|
||||||
|
tabs.set_hexpand(true);
|
||||||
tabs.hide();
|
tabs.hide();
|
||||||
|
|
||||||
let state = Rc::new(RefCell::new(State::new()));
|
let state = Rc::new(RefCell::new(State::new()));
|
||||||
@ -113,7 +122,33 @@ impl Tabline {
|
|||||||
let title = gtk::Label::new(None);
|
let title = gtk::Label::new(None);
|
||||||
title.set_ellipsize(pango::EllipsizeMode::Middle);
|
title.set_ellipsize(pango::EllipsizeMode::Middle);
|
||||||
title.set_width_chars(25);
|
title.set_width_chars(25);
|
||||||
self.tabs.append_page(&empty, Some(&title));
|
let close_btn = gtk::Button::new_from_icon_name(
|
||||||
|
"window-close-symbolic",
|
||||||
|
gtk::IconSize::Menu.into(),
|
||||||
|
);
|
||||||
|
close_btn.set_relief(gtk::ReliefStyle::None);
|
||||||
|
close_btn.get_style_context().unwrap().add_class("small-button");
|
||||||
|
close_btn.set_focus_on_click(false);
|
||||||
|
let label_box = gtk::Box::new(gtk::Orientation::Horizontal, 0);
|
||||||
|
label_box.pack_start(&title, true, false, 0);
|
||||||
|
label_box.pack_start(&close_btn, false, false, 0);
|
||||||
|
title.show();
|
||||||
|
close_btn.show();
|
||||||
|
self.tabs.append_page(&empty, Some(&label_box));
|
||||||
|
|
||||||
|
let tabs = self.tabs.clone();
|
||||||
|
let state_ref = Rc::clone(&self.state);
|
||||||
|
close_btn.connect_clicked(move |btn| {
|
||||||
|
let current_label = btn
|
||||||
|
.get_parent().unwrap();
|
||||||
|
for i in 0..tabs.get_n_pages() {
|
||||||
|
let page = tabs.get_nth_page(Some(i)).unwrap();
|
||||||
|
let label = tabs.get_tab_label(&page).unwrap();
|
||||||
|
if label == current_label {
|
||||||
|
state_ref.borrow().close_tab(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if count > tabs.len() {
|
} else if count > tabs.len() {
|
||||||
for _ in tabs.len()..count {
|
for _ in tabs.len()..count {
|
||||||
@ -126,6 +161,12 @@ impl Tabline {
|
|||||||
let tab_label = self.tabs
|
let tab_label = self.tabs
|
||||||
.get_tab_label(&tab_child.unwrap())
|
.get_tab_label(&tab_child.unwrap())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.downcast::<gtk::Box>()
|
||||||
|
.unwrap()
|
||||||
|
.get_children()
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
.downcast::<gtk::Label>()
|
.downcast::<gtk::Label>()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
tab_label.set_text(tab.1.as_ref().unwrap_or(&"??".to_owned()));
|
tab_label.set_text(tab.1.as_ref().unwrap_or(&"??".to_owned()));
|
||||||
|
248
src/ui.rs
248
src/ui.rs
@ -1,22 +1,28 @@
|
|||||||
use std::cell::{Ref, RefCell, RefMut};
|
use std::cell::{Ref, RefCell, RefMut};
|
||||||
use std::{env, thread};
|
use std::{env, thread};
|
||||||
|
use std::path::Path;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use gdk;
|
use gdk;
|
||||||
use gtk;
|
use gtk;
|
||||||
use gtk_sys;
|
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use gtk::{AboutDialog, ApplicationWindow, HeaderBar, Image, SettingsExt, ToolButton};
|
use gtk::{AboutDialog, ApplicationWindow, Button, HeaderBar, Orientation, Paned, SettingsExt};
|
||||||
use gio::prelude::*;
|
use gio::prelude::*;
|
||||||
use gio::{Menu, MenuExt, MenuItem, SimpleAction};
|
use gio::{Menu, MenuExt, MenuItem, SimpleAction};
|
||||||
|
use glib::variant::FromVariant;
|
||||||
|
|
||||||
use toml;
|
use toml;
|
||||||
|
|
||||||
|
use neovim_lib::Value;
|
||||||
|
|
||||||
use settings::{Settings, SettingsLoader};
|
use settings::{Settings, SettingsLoader};
|
||||||
use shell::{self, Shell, ShellOptions};
|
use shell::{self, Shell, ShellOptions};
|
||||||
use shell_dlg;
|
use shell_dlg;
|
||||||
use project::Projects;
|
use project::Projects;
|
||||||
use plug_manager;
|
use plug_manager;
|
||||||
|
use file_browser::FileBrowserWidget;
|
||||||
|
use subscriptions::SubscriptionHandle;
|
||||||
|
|
||||||
macro_rules! clone {
|
macro_rules! clone {
|
||||||
(@param _) => ( _ );
|
(@param _) => ( _ );
|
||||||
@ -37,6 +43,7 @@ macro_rules! clone {
|
|||||||
|
|
||||||
const DEFAULT_WIDTH: i32 = 800;
|
const DEFAULT_WIDTH: i32 = 800;
|
||||||
const DEFAULT_HEIGHT: i32 = 600;
|
const DEFAULT_HEIGHT: i32 = 600;
|
||||||
|
const DEFAULT_SIDEBAR_WIDTH: i32 = 200;
|
||||||
|
|
||||||
pub struct Ui {
|
pub struct Ui {
|
||||||
initialized: bool,
|
initialized: bool,
|
||||||
@ -45,21 +52,30 @@ pub struct Ui {
|
|||||||
shell: Rc<RefCell<Shell>>,
|
shell: Rc<RefCell<Shell>>,
|
||||||
projects: Rc<RefCell<Projects>>,
|
projects: Rc<RefCell<Projects>>,
|
||||||
plug_manager: Arc<UiMutex<plug_manager::Manager>>,
|
plug_manager: Arc<UiMutex<plug_manager::Manager>>,
|
||||||
|
file_browser: Arc<UiMutex<FileBrowserWidget>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Components {
|
pub struct Components {
|
||||||
window: Option<ApplicationWindow>,
|
window: Option<ApplicationWindow>,
|
||||||
window_state: WindowState,
|
window_state: WindowState,
|
||||||
open_btn: ToolButton,
|
open_btn: Button,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Components {
|
impl Components {
|
||||||
fn new() -> Components {
|
fn new() -> Components {
|
||||||
let save_image =
|
let open_btn = Button::new();
|
||||||
Image::new_from_icon_name("document-open", gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32);
|
let open_btn_box = gtk::Box::new(gtk::Orientation::Horizontal, 3);
|
||||||
|
open_btn_box.pack_start(>k::Label::new("Open"), false, false, 3);
|
||||||
|
open_btn_box.pack_start(
|
||||||
|
>k::Image::new_from_icon_name("pan-down-symbolic", gtk::IconSize::Menu.into()),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
3,
|
||||||
|
);
|
||||||
|
open_btn.add(&open_btn_box);
|
||||||
|
open_btn.set_can_focus(false);
|
||||||
Components {
|
Components {
|
||||||
open_btn: ToolButton::new(Some(&save_image), "Open"),
|
open_btn,
|
||||||
window: None,
|
window: None,
|
||||||
window_state: WindowState::load(),
|
window_state: WindowState::load(),
|
||||||
}
|
}
|
||||||
@ -79,6 +95,7 @@ impl Ui {
|
|||||||
let plug_manager = plug_manager::Manager::new();
|
let plug_manager = plug_manager::Manager::new();
|
||||||
|
|
||||||
let plug_manager = Arc::new(UiMutex::new(plug_manager));
|
let plug_manager = Arc::new(UiMutex::new(plug_manager));
|
||||||
|
let file_browser = Arc::new(UiMutex::new(FileBrowserWidget::new()));
|
||||||
let comps = Arc::new(UiMutex::new(Components::new()));
|
let comps = Arc::new(UiMutex::new(Components::new()));
|
||||||
let settings = Rc::new(RefCell::new(Settings::new()));
|
let settings = Rc::new(RefCell::new(Settings::new()));
|
||||||
let shell = Rc::new(RefCell::new(Shell::new(settings.clone(), options)));
|
let shell = Rc::new(RefCell::new(Shell::new(settings.clone(), options)));
|
||||||
@ -93,6 +110,7 @@ impl Ui {
|
|||||||
settings,
|
settings,
|
||||||
projects,
|
projects,
|
||||||
plug_manager,
|
plug_manager,
|
||||||
|
file_browser,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,6 +124,7 @@ impl Ui {
|
|||||||
settings.init();
|
settings.init();
|
||||||
|
|
||||||
let window = ApplicationWindow::new(app);
|
let window = ApplicationWindow::new(app);
|
||||||
|
let main = Paned::new(Orientation::Horizontal);
|
||||||
|
|
||||||
{
|
{
|
||||||
// initialize window from comps
|
// initialize window from comps
|
||||||
@ -126,48 +145,6 @@ impl Ui {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client side decorations including the toolbar are disabled via NVIM_GTK_NO_HEADERBAR=1
|
|
||||||
let use_header_bar = env::var("NVIM_GTK_NO_HEADERBAR")
|
|
||||||
.map(|opt| opt.trim() != "1")
|
|
||||||
.unwrap_or(true);
|
|
||||||
|
|
||||||
if app.prefers_app_menu() || use_header_bar {
|
|
||||||
self.create_main_menu(app, &window);
|
|
||||||
}
|
|
||||||
|
|
||||||
if use_header_bar {
|
|
||||||
let header_bar = HeaderBar::new();
|
|
||||||
|
|
||||||
let projects = self.projects.clone();
|
|
||||||
header_bar.pack_start(&comps.open_btn);
|
|
||||||
comps
|
|
||||||
.open_btn
|
|
||||||
.connect_clicked(move |_| projects.borrow_mut().show());
|
|
||||||
|
|
||||||
let save_image = Image::new_from_icon_name(
|
|
||||||
"document-save",
|
|
||||||
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32,
|
|
||||||
);
|
|
||||||
let save_btn = ToolButton::new(Some(&save_image), "Save");
|
|
||||||
|
|
||||||
let shell = self.shell.clone();
|
|
||||||
save_btn.connect_clicked(move |_| shell.borrow_mut().edit_save_all());
|
|
||||||
header_bar.pack_start(&save_btn);
|
|
||||||
|
|
||||||
let paste_image = Image::new_from_icon_name(
|
|
||||||
"edit-paste",
|
|
||||||
gtk_sys::GTK_ICON_SIZE_SMALL_TOOLBAR as i32,
|
|
||||||
);
|
|
||||||
let paste_btn = ToolButton::new(Some(&paste_image), "Paste");
|
|
||||||
let shell = self.shell.clone();
|
|
||||||
paste_btn.connect_clicked(move |_| shell.borrow_mut().edit_paste());
|
|
||||||
header_bar.pack_start(&paste_btn);
|
|
||||||
|
|
||||||
header_bar.set_show_close_button(true);
|
|
||||||
|
|
||||||
window.set_titlebar(Some(&header_bar));
|
|
||||||
}
|
|
||||||
|
|
||||||
if restore_win_state {
|
if restore_win_state {
|
||||||
if comps.window_state.is_maximized {
|
if comps.window_state.is_maximized {
|
||||||
window.maximize();
|
window.maximize();
|
||||||
@ -177,15 +154,51 @@ impl Ui {
|
|||||||
comps.window_state.current_width,
|
comps.window_state.current_width,
|
||||||
comps.window_state.current_height,
|
comps.window_state.current_height,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
main.set_position(comps.window_state.sidebar_width);
|
||||||
} else {
|
} else {
|
||||||
window.set_default_size(DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
window.set_default_size(DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
||||||
|
main.set_position(DEFAULT_SIDEBAR_WIDTH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Client side decorations including the toolbar are disabled via NVIM_GTK_NO_HEADERBAR=1
|
||||||
|
let use_header_bar = env::var("NVIM_GTK_NO_HEADERBAR")
|
||||||
|
.map(|opt| opt.trim() != "1")
|
||||||
|
.unwrap_or(true);
|
||||||
|
|
||||||
|
if app.prefers_app_menu() || use_header_bar {
|
||||||
|
self.create_main_menu(app, &window);
|
||||||
|
}
|
||||||
|
|
||||||
|
let update_subtitle = if use_header_bar {
|
||||||
|
Some(self.create_header_bar())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let show_sidebar_action =
|
||||||
|
SimpleAction::new_stateful("show-sidebar", None, &false.to_variant());
|
||||||
|
let file_browser_ref = self.file_browser.clone();
|
||||||
let comps_ref = self.comps.clone();
|
let comps_ref = self.comps.clone();
|
||||||
window.connect_size_allocate(move |window, _| {
|
show_sidebar_action.connect_change_state(move |action, value| {
|
||||||
gtk_window_size_allocate(window, &mut *comps_ref.borrow_mut())
|
if let Some(ref value) = *value {
|
||||||
|
action.set_state(value);
|
||||||
|
let is_active = value.get::<bool>().unwrap();
|
||||||
|
file_browser_ref.borrow().set_visible(is_active);
|
||||||
|
comps_ref.borrow_mut().window_state.show_sidebar = is_active;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
app.add_action(&show_sidebar_action);
|
||||||
|
|
||||||
|
let comps_ref = self.comps.clone();
|
||||||
|
window.connect_size_allocate(clone!(main => move |window, _| {
|
||||||
|
gtk_window_size_allocate(
|
||||||
|
window,
|
||||||
|
&mut *comps_ref.borrow_mut(),
|
||||||
|
&main,
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
|
||||||
let comps_ref = self.comps.clone();
|
let comps_ref = self.comps.clone();
|
||||||
window.connect_window_state_event(move |_, event| {
|
window.connect_window_state_event(move |_, event| {
|
||||||
@ -199,10 +212,44 @@ impl Ui {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let shell = self.shell.borrow();
|
let shell = self.shell.borrow();
|
||||||
window.add(&**shell);
|
let file_browser = self.file_browser.borrow();
|
||||||
|
main.pack1(&**file_browser, false, false);
|
||||||
|
main.pack2(&**shell, true, false);
|
||||||
|
|
||||||
|
window.add(&main);
|
||||||
|
|
||||||
window.show_all();
|
window.show_all();
|
||||||
window.set_title("NeovimGtk");
|
|
||||||
|
if restore_win_state {
|
||||||
|
// Hide sidebar, if it wasn't shown last time.
|
||||||
|
// Has to be done after show_all(), so it won't be shown again.
|
||||||
|
let show_sidebar = self.comps.borrow().window_state.show_sidebar;
|
||||||
|
show_sidebar_action.change_state(&show_sidebar.to_variant());
|
||||||
|
}
|
||||||
|
|
||||||
|
let comps_ref = self.comps.clone();
|
||||||
|
let update_title = shell.state.borrow().subscribe(
|
||||||
|
"BufEnter,DirChanged",
|
||||||
|
&["expand('%:p')", "getcwd()"],
|
||||||
|
move |args| {
|
||||||
|
let comps = comps_ref.borrow();
|
||||||
|
let window = comps.window.as_ref().unwrap();
|
||||||
|
let file_path = &args[0];
|
||||||
|
let dir = Path::new(&args[1]);
|
||||||
|
let filename = if file_path.is_empty() {
|
||||||
|
"[No Name]"
|
||||||
|
} else if let Some(rel_path) = Path::new(&file_path)
|
||||||
|
.strip_prefix(&dir)
|
||||||
|
.ok()
|
||||||
|
.and_then(|p| p.to_str())
|
||||||
|
{
|
||||||
|
rel_path
|
||||||
|
} else {
|
||||||
|
&file_path
|
||||||
|
};
|
||||||
|
window.set_title(filename);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let comps_ref = self.comps.clone();
|
let comps_ref = self.comps.clone();
|
||||||
let shell_ref = self.shell.clone();
|
let shell_ref = self.shell.clone();
|
||||||
@ -220,12 +267,83 @@ impl Ui {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let state_ref = self.shell.borrow().state.clone();
|
let state_ref = self.shell.borrow().state.clone();
|
||||||
|
let file_browser_ref = self.file_browser.clone();
|
||||||
let plug_manager_ref = self.plug_manager.clone();
|
let plug_manager_ref = self.plug_manager.clone();
|
||||||
shell.set_nvim_started_cb(Some(move || {
|
shell.set_nvim_started_cb(Some(move || {
|
||||||
|
let state = state_ref.borrow();
|
||||||
plug_manager_ref
|
plug_manager_ref
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.init_nvim_client(state_ref.borrow().nvim_clone());
|
.init_nvim_client(state_ref.borrow().nvim_clone());
|
||||||
|
file_browser_ref.borrow_mut().init(&state);
|
||||||
|
state.set_autocmds();
|
||||||
|
state.run_now(&update_title);
|
||||||
|
if let Some(ref update_subtitle) = update_subtitle {
|
||||||
|
state.run_now(&update_subtitle);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
let sidebar_action = UiMutex::new(show_sidebar_action);
|
||||||
|
shell.set_nvim_command_cb(Some(move |args: Vec<Value>| {
|
||||||
|
if let Some(cmd) = args[0].as_str() {
|
||||||
|
match cmd {
|
||||||
|
"ToggleSidebar" => {
|
||||||
|
let action = sidebar_action.borrow();
|
||||||
|
let state = !bool::from_variant(&action.get_state().unwrap()).unwrap();
|
||||||
|
action.change_state(&state.to_variant());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_header_bar(&self) -> SubscriptionHandle {
|
||||||
|
let header_bar = HeaderBar::new();
|
||||||
|
let comps = self.comps.borrow();
|
||||||
|
let window = comps.window.as_ref().unwrap();
|
||||||
|
|
||||||
|
let projects = self.projects.clone();
|
||||||
|
header_bar.pack_start(&comps.open_btn);
|
||||||
|
comps
|
||||||
|
.open_btn
|
||||||
|
.connect_clicked(move |_| projects.borrow_mut().show());
|
||||||
|
|
||||||
|
let new_tab_btn =
|
||||||
|
Button::new_from_icon_name("tab-new-symbolic", gtk::IconSize::SmallToolbar.into());
|
||||||
|
let shell_ref = Rc::clone(&self.shell);
|
||||||
|
new_tab_btn.connect_clicked(move |_| shell_ref.borrow_mut().new_tab());
|
||||||
|
new_tab_btn.set_can_focus(false);
|
||||||
|
new_tab_btn.set_tooltip_text("Open a new tab");
|
||||||
|
header_bar.pack_start(&new_tab_btn);
|
||||||
|
|
||||||
|
let paste_btn =
|
||||||
|
Button::new_from_icon_name("edit-paste-symbolic", gtk::IconSize::SmallToolbar.into());
|
||||||
|
let shell = self.shell.clone();
|
||||||
|
paste_btn.connect_clicked(move |_| shell.borrow_mut().edit_paste());
|
||||||
|
paste_btn.set_can_focus(false);
|
||||||
|
paste_btn.set_tooltip_text("Paste from clipboard");
|
||||||
|
header_bar.pack_end(&paste_btn);
|
||||||
|
|
||||||
|
let save_btn = Button::new_with_label("Save All");
|
||||||
|
let shell = self.shell.clone();
|
||||||
|
save_btn.connect_clicked(move |_| shell.borrow_mut().edit_save_all());
|
||||||
|
save_btn.set_can_focus(false);
|
||||||
|
header_bar.pack_end(&save_btn);
|
||||||
|
|
||||||
|
header_bar.set_show_close_button(true);
|
||||||
|
|
||||||
|
window.set_titlebar(Some(&header_bar));
|
||||||
|
|
||||||
|
let shell = self.shell.borrow();
|
||||||
|
let update_subtitle = shell.state.borrow().subscribe(
|
||||||
|
"DirChanged",
|
||||||
|
&["getcwd()"],
|
||||||
|
move |args| {
|
||||||
|
header_bar.set_subtitle(&*args[0]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
update_subtitle
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_main_menu(&self, app: >k::Application, window: >k::ApplicationWindow) {
|
fn create_main_menu(&self, app: >k::Application, window: >k::ApplicationWindow) {
|
||||||
@ -237,6 +355,10 @@ impl Ui {
|
|||||||
section.append_item(&MenuItem::new("New Window", "app.new-window"));
|
section.append_item(&MenuItem::new("New Window", "app.new-window"));
|
||||||
menu.append_section(None, §ion);
|
menu.append_section(None, §ion);
|
||||||
|
|
||||||
|
let section = Menu::new();
|
||||||
|
section.append_item(&MenuItem::new("Sidebar", "app.show-sidebar"));
|
||||||
|
menu.append_section(None, §ion);
|
||||||
|
|
||||||
let section = Menu::new();
|
let section = Menu::new();
|
||||||
section.append_item(&MenuItem::new("Plugins", "app.Plugins"));
|
section.append_item(&MenuItem::new("Plugins", "app.Plugins"));
|
||||||
section.append_item(&MenuItem::new("About", "app.HelpAbout"));
|
section.append_item(&MenuItem::new("About", "app.HelpAbout"));
|
||||||
@ -293,12 +415,19 @@ fn gtk_delete(comps: &UiMutex<Components>, shell: &RefCell<Shell>) -> Inhibit {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gtk_window_size_allocate(app_window: >k::ApplicationWindow, comps: &mut Components) {
|
fn gtk_window_size_allocate(
|
||||||
|
app_window: >k::ApplicationWindow,
|
||||||
|
comps: &mut Components,
|
||||||
|
main: &Paned,
|
||||||
|
) {
|
||||||
if !app_window.is_maximized() {
|
if !app_window.is_maximized() {
|
||||||
let (current_width, current_height) = app_window.get_size();
|
let (current_width, current_height) = app_window.get_size();
|
||||||
comps.window_state.current_width = current_width;
|
comps.window_state.current_width = current_width;
|
||||||
comps.window_state.current_height = current_height;
|
comps.window_state.current_height = current_height;
|
||||||
}
|
}
|
||||||
|
if comps.window_state.show_sidebar {
|
||||||
|
comps.window_state.sidebar_width = main.get_position();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gtk_window_state_event(event: &gdk::EventWindowState, comps: &mut Components) {
|
fn gtk_window_state_event(event: &gdk::EventWindowState, comps: &mut Components) {
|
||||||
@ -312,6 +441,8 @@ struct WindowState {
|
|||||||
current_width: i32,
|
current_width: i32,
|
||||||
current_height: i32,
|
current_height: i32,
|
||||||
is_maximized: bool,
|
is_maximized: bool,
|
||||||
|
show_sidebar: bool,
|
||||||
|
sidebar_width: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowState {
|
impl WindowState {
|
||||||
@ -320,6 +451,8 @@ impl WindowState {
|
|||||||
current_width: DEFAULT_WIDTH,
|
current_width: DEFAULT_WIDTH,
|
||||||
current_height: DEFAULT_HEIGHT,
|
current_height: DEFAULT_HEIGHT,
|
||||||
is_maximized: false,
|
is_maximized: false,
|
||||||
|
show_sidebar: false,
|
||||||
|
sidebar_width: DEFAULT_SIDEBAR_WIDTH,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -353,6 +486,13 @@ impl<T> UiMutex<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl <T> UiMutex<T> {
|
||||||
|
pub fn replace(&self, t: T) -> T {
|
||||||
|
self.assert_ui_thread();
|
||||||
|
self.data.replace(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: ?Sized> UiMutex<T> {
|
impl<T: ?Sized> UiMutex<T> {
|
||||||
pub fn borrow(&self) -> Ref<T> {
|
pub fn borrow(&self) -> Ref<T> {
|
||||||
self.assert_ui_thread();
|
self.assert_ui_thread();
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use color::Color;
|
use color::Color;
|
||||||
|
use neovim_lib::Value;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Attrs {
|
pub struct Attrs {
|
||||||
@ -28,6 +31,38 @@ impl Attrs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_value_map(attrs: &HashMap<String, Value>) -> Attrs {
|
||||||
|
let mut model_attrs = Attrs::new();
|
||||||
|
|
||||||
|
for (ref key, ref val) in attrs {
|
||||||
|
match key.as_ref() {
|
||||||
|
"foreground" => {
|
||||||
|
if let Some(fg) = val.as_u64() {
|
||||||
|
model_attrs.foreground = Some(Color::from_indexed_color(fg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"background" => {
|
||||||
|
if let Some(bg) = val.as_u64() {
|
||||||
|
model_attrs.background = Some(Color::from_indexed_color(bg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"special" => {
|
||||||
|
if let Some(bg) = val.as_u64() {
|
||||||
|
model_attrs.special = Some(Color::from_indexed_color(bg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"reverse" => model_attrs.reverse = true,
|
||||||
|
"bold" => model_attrs.bold = true,
|
||||||
|
"italic" => model_attrs.italic = true,
|
||||||
|
"underline" => model_attrs.underline = true,
|
||||||
|
"undercurl" => model_attrs.undercurl = true,
|
||||||
|
attr_key => error!("unknown attribute {}", attr_key),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
model_attrs
|
||||||
|
}
|
||||||
|
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
self.italic = false;
|
self.italic = false;
|
||||||
self.bold = false;
|
self.bold = false;
|
||||||
|
@ -2,11 +2,13 @@ mod cell;
|
|||||||
mod line;
|
mod line;
|
||||||
mod item;
|
mod item;
|
||||||
mod model_rect;
|
mod model_rect;
|
||||||
|
mod model_layout;
|
||||||
|
|
||||||
pub use self::cell::{Cell, Attrs};
|
pub use self::cell::{Cell, Attrs};
|
||||||
pub use self::line::{Line, StyledLine};
|
pub use self::line::{Line, StyledLine};
|
||||||
pub use self::item::Item;
|
pub use self::item::Item;
|
||||||
pub use self::model_rect::{ModelRect, ModelRectVec};
|
pub use self::model_rect::{ModelRect, ModelRectVec};
|
||||||
|
pub use self::model_layout::ModelLayout;
|
||||||
|
|
||||||
|
|
||||||
pub struct UiModel {
|
pub struct UiModel {
|
||||||
@ -91,16 +93,16 @@ impl UiModel {
|
|||||||
(self.cur_row, self.cur_col)
|
(self.cur_row, self.cur_col)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn put(&mut self, text: &str, attrs: Option<&Attrs>) -> ModelRect {
|
pub fn put(&mut self, ch: char, double_width: bool, attrs: Option<&Attrs>) -> ModelRect {
|
||||||
let mut changed_region = self.cur_point();
|
let mut changed_region = self.cur_point();
|
||||||
let line = &mut self.model[self.cur_row];
|
let line = &mut self.model[self.cur_row];
|
||||||
line.dirty_line = true;
|
line.dirty_line = true;
|
||||||
|
|
||||||
let cell = &mut line[self.cur_col];
|
let cell = &mut line[self.cur_col];
|
||||||
|
|
||||||
cell.ch = text.chars().last().unwrap_or(' ');
|
cell.ch = ch;
|
||||||
cell.attrs = attrs.map(Attrs::clone).unwrap_or_else(Attrs::new);
|
cell.attrs = attrs.map(Attrs::clone).unwrap_or_else(Attrs::new);
|
||||||
cell.attrs.double_width = text.is_empty();
|
cell.attrs.double_width = double_width;
|
||||||
cell.dirty = true;
|
cell.dirty = true;
|
||||||
self.cur_col += 1;
|
self.cur_col += 1;
|
||||||
if self.cur_col >= self.columns {
|
if self.cur_col >= self.columns {
|
||||||
@ -119,6 +121,16 @@ impl UiModel {
|
|||||||
self.right = right as usize;
|
self.right = right as usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Copy rows from 0 to to_row, col from 0 self.columns
|
||||||
|
///
|
||||||
|
/// Don't do any validation!
|
||||||
|
pub fn copy_rows(&self, target: &mut UiModel, to_row: usize) {
|
||||||
|
for (row_idx, line) in self.model[0..to_row + 1].iter().enumerate() {
|
||||||
|
let mut target_row = &mut target.model[row_idx];
|
||||||
|
line.copy_to(target_row, 0, self.columns - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn copy_row(&mut self, target_row: i64, offset: i64, left_col: usize, right_col: usize) {
|
fn copy_row(&mut self, target_row: i64, offset: i64, left_col: usize, right_col: usize) {
|
||||||
debug_assert_ne!(0, offset);
|
debug_assert_ne!(0, offset);
|
||||||
@ -293,7 +305,7 @@ mod tests {
|
|||||||
|
|
||||||
model.set_cursor(1, 1);
|
model.set_cursor(1, 1);
|
||||||
|
|
||||||
let rect = model.put(" ", None);
|
let rect = model.put(' ', false, None);
|
||||||
|
|
||||||
assert_eq!(1, rect.top);
|
assert_eq!(1, rect.top);
|
||||||
assert_eq!(1, rect.left);
|
assert_eq!(1, rect.left);
|
||||||
|
251
src/ui_model/model_layout.rs
Normal file
251
src/ui_model/model_layout.rs
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
use std::cmp::max;
|
||||||
|
|
||||||
|
use unicode_width::UnicodeWidthChar;
|
||||||
|
|
||||||
|
use ui_model::{Attrs, UiModel};
|
||||||
|
|
||||||
|
pub struct ModelLayout {
|
||||||
|
pub model: UiModel,
|
||||||
|
rows_filled: usize,
|
||||||
|
cols_filled: usize,
|
||||||
|
lines: Vec<Vec<(Option<Attrs>, Vec<char>)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModelLayout {
|
||||||
|
const ROWS_STEP: usize = 10;
|
||||||
|
|
||||||
|
pub fn new(columns: u64) -> Self {
|
||||||
|
ModelLayout {
|
||||||
|
model: UiModel::new(ModelLayout::ROWS_STEP as u64, columns),
|
||||||
|
rows_filled: 0,
|
||||||
|
cols_filled: 0,
|
||||||
|
lines: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn layout_append(&mut self, mut lines: Vec<Vec<(Option<Attrs>, Vec<char>)>>) {
|
||||||
|
let rows_filled = self.rows_filled;
|
||||||
|
let take_from = self.lines.len();
|
||||||
|
|
||||||
|
self.lines.append(&mut lines);
|
||||||
|
|
||||||
|
self.layout_replace(rows_filled, take_from);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn layout(&mut self, lines: Vec<Vec<(Option<Attrs>, Vec<char>)>>) {
|
||||||
|
self.lines = lines;
|
||||||
|
self.layout_replace(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_cursor(&mut self, col: usize) {
|
||||||
|
let row = if self.rows_filled > 0 {
|
||||||
|
self.rows_filled - 1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
self.model.set_cursor(row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> (usize, usize) {
|
||||||
|
(
|
||||||
|
max(self.cols_filled, self.model.get_cursor().1 + 1),
|
||||||
|
self.rows_filled,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_model_size(&mut self, rows: usize) {
|
||||||
|
if rows > self.model.rows {
|
||||||
|
let model_cols = self.model.columns;
|
||||||
|
let model_rows = ((rows / (ModelLayout::ROWS_STEP + 1)) + 1) * ModelLayout::ROWS_STEP;
|
||||||
|
let (cur_row, cur_col) = self.model.get_cursor();
|
||||||
|
|
||||||
|
let mut model = UiModel::new(model_rows as u64, model_cols as u64);
|
||||||
|
self.model.copy_rows(&mut model, self.rows_filled - 1);
|
||||||
|
model.set_cursor(cur_row, cur_col);
|
||||||
|
self.model = model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_char(&mut self, c: &str, shift: bool) {
|
||||||
|
if c.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ch = c.chars().next().unwrap();
|
||||||
|
let (row, col) = self.model.get_cursor();
|
||||||
|
|
||||||
|
if shift {
|
||||||
|
self.insert_into_lines(ch);
|
||||||
|
self.layout_replace(0, 0);
|
||||||
|
} else {
|
||||||
|
self.model.put(ch, false, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.model.set_cursor(row, col);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert_into_lines(&mut self, ch: char) {
|
||||||
|
let line = &mut self.lines[0];
|
||||||
|
|
||||||
|
let cur_col = self.model.cur_col;
|
||||||
|
|
||||||
|
let mut col_idx = 0;
|
||||||
|
for &mut (_, ref mut chars) in line {
|
||||||
|
if cur_col < col_idx + chars.len() {
|
||||||
|
let col_sub_idx = cur_col - col_idx;
|
||||||
|
chars.insert(col_sub_idx, ch);
|
||||||
|
} else {
|
||||||
|
col_idx += chars.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrap all lines into model
|
||||||
|
///
|
||||||
|
/// returns actual width
|
||||||
|
fn layout_replace(&mut self, row_offset: usize, take_from: usize) {
|
||||||
|
let rows = ModelLayout::count_lines(&self.lines[take_from..], self.model.columns);
|
||||||
|
|
||||||
|
self.check_model_size(rows + row_offset);
|
||||||
|
self.rows_filled = rows + row_offset;
|
||||||
|
|
||||||
|
let lines = &self.lines[take_from..];
|
||||||
|
|
||||||
|
let mut max_col_idx = 0;
|
||||||
|
let mut col_idx = 0;
|
||||||
|
let mut row_idx = row_offset;
|
||||||
|
for content in lines {
|
||||||
|
for &(ref attr, ref ch_list) in content {
|
||||||
|
for ch in ch_list {
|
||||||
|
let ch_width = ch.width().unwrap_or(1);
|
||||||
|
|
||||||
|
if col_idx + ch_width > self.model.columns {
|
||||||
|
col_idx = 0;
|
||||||
|
row_idx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.model.set_cursor(row_idx, col_idx as usize);
|
||||||
|
self.model.put(*ch, false, attr.as_ref());
|
||||||
|
if ch_width > 1 {
|
||||||
|
self.model.put(' ', true, attr.as_ref());
|
||||||
|
}
|
||||||
|
|
||||||
|
if max_col_idx < col_idx {
|
||||||
|
max_col_idx = col_idx + ch_width - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
col_idx += ch_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if col_idx < self.model.columns {
|
||||||
|
self.model.model[row_idx].clear(col_idx, self.model.columns - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
row_idx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.rows_filled == 1 {
|
||||||
|
self.cols_filled = max_col_idx + 1;
|
||||||
|
} else {
|
||||||
|
self.cols_filled = max(self.cols_filled, max_col_idx + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count_lines(lines: &[Vec<(Option<Attrs>, Vec<char>)>], max_columns: usize) -> usize {
|
||||||
|
let mut row_count = 0;
|
||||||
|
|
||||||
|
for line in lines {
|
||||||
|
let len: usize = line.iter().map(|c| c.1.len()).sum();
|
||||||
|
row_count += len / (max_columns + 1) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
row_count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_count_lines() {
|
||||||
|
let lines = vec![vec![(None, vec!['a'; 5])]];
|
||||||
|
|
||||||
|
let rows = ModelLayout::count_lines(&lines, 4);
|
||||||
|
assert_eq!(2, rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_resize() {
|
||||||
|
let lines = vec![vec![(None, vec!['a'; 5])]; ModelLayout::ROWS_STEP];
|
||||||
|
let mut model = ModelLayout::new(5);
|
||||||
|
|
||||||
|
model.layout(lines.clone());
|
||||||
|
let (cols, rows) = model.size();
|
||||||
|
assert_eq!(5, cols);
|
||||||
|
assert_eq!(ModelLayout::ROWS_STEP, rows);
|
||||||
|
|
||||||
|
model.layout_append(lines);
|
||||||
|
let (cols, rows) = model.size();
|
||||||
|
assert_eq!(5, cols);
|
||||||
|
assert_eq!(ModelLayout::ROWS_STEP * 2, rows);
|
||||||
|
assert_eq!(ModelLayout::ROWS_STEP * 2, model.model.rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cols_filled() {
|
||||||
|
let lines = vec![vec![(None, vec!['a'; 3])]; 1];
|
||||||
|
let mut model = ModelLayout::new(5);
|
||||||
|
|
||||||
|
model.layout(lines);
|
||||||
|
let (cols, _) = model.size();
|
||||||
|
assert_eq!(4, cols); // size is 3 and 4 - is with cursor position
|
||||||
|
|
||||||
|
let lines = vec![vec![(None, vec!['a'; 2])]; 1];
|
||||||
|
|
||||||
|
model.layout_append(lines);
|
||||||
|
let (cols, _) = model.size();
|
||||||
|
assert_eq!(3, cols);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_insert_shift() {
|
||||||
|
let lines = vec![vec![(None, vec!['a'; 3])]; 1];
|
||||||
|
let mut model = ModelLayout::new(5);
|
||||||
|
model.layout(lines);
|
||||||
|
model.set_cursor(1);
|
||||||
|
|
||||||
|
model.insert_char("b", true);
|
||||||
|
|
||||||
|
let (cols, _) = model.size();
|
||||||
|
assert_eq!(4, cols);
|
||||||
|
assert_eq!('b', model.model.model()[0].line[1].ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_insert_no_shift() {
|
||||||
|
let lines = vec![vec![(None, vec!['a'; 3])]; 1];
|
||||||
|
let mut model = ModelLayout::new(5);
|
||||||
|
model.layout(lines);
|
||||||
|
model.set_cursor(1);
|
||||||
|
|
||||||
|
model.insert_char("b", false);
|
||||||
|
|
||||||
|
let (cols, _) = model.size();
|
||||||
|
assert_eq!(3, cols);
|
||||||
|
assert_eq!('b', model.model.model()[0].line[1].ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_double_width() {
|
||||||
|
let lines = vec![vec![(None, vec!['あ'; 3])]; 1];
|
||||||
|
let mut model = ModelLayout::new(7);
|
||||||
|
model.layout(lines);
|
||||||
|
model.set_cursor(1);
|
||||||
|
|
||||||
|
let (cols, rows) = model.size();
|
||||||
|
assert_eq!(1, rows);
|
||||||
|
assert_eq!(6, cols);
|
||||||
|
}
|
||||||
|
}
|
@ -56,8 +56,8 @@ pub struct ModelRect {
|
|||||||
|
|
||||||
impl ModelRect {
|
impl ModelRect {
|
||||||
pub fn new(top: usize, bot: usize, left: usize, right: usize) -> ModelRect {
|
pub fn new(top: usize, bot: usize, left: usize, right: usize) -> ModelRect {
|
||||||
debug_assert!(top <= bot);
|
debug_assert!(top <= bot, "{} <= {}", top, bot);
|
||||||
debug_assert!(left <= right);
|
debug_assert!(left <= right, "{} <= {}", left, right);
|
||||||
|
|
||||||
ModelRect {
|
ModelRect {
|
||||||
top: top,
|
top: top,
|
||||||
|
Loading…
Reference in New Issue
Block a user