Support completing commands, usernames, and room names (#44)

This commit is contained in:
Ulyssa 2023-03-01 18:46:33 -08:00
parent e3be8c16cb
commit 0ed1d53946
No known key found for this signature in database
GPG key ID: 1B3965A3D18B9B64
13 changed files with 491 additions and 91 deletions

View file

@ -1,5 +1,4 @@
use std::cmp::{Ord, Ordering, PartialOrd};
use std::collections::hash_map::Entry;
use matrix_sdk::{
encryption::verification::{format_emojis, SasVerification},
@ -45,7 +44,9 @@ use modalkit::{
ScrollStyle,
ViewportContext,
WordStyle,
WriteFlags,
},
completion::CompletionList,
},
widgets::{
list::{List, ListCursor, ListItem, ListState},
@ -469,6 +470,19 @@ impl WindowOps<IambInfo> for IambWindow {
delegate!(self, w => w.close(flags, store))
}
fn write(
&mut self,
path: Option<&str>,
flags: WriteFlags,
store: &mut ProgramStore,
) -> IambResult<EditInfo> {
delegate!(self, w => w.write(path, flags, store))
}
fn get_completions(&self) -> Option<CompletionList> {
delegate!(self, w => w.get_completions())
}
fn get_cursor_word(&self, style: &WordStyle) -> Option<String> {
delegate!(self, w => w.get_cursor_word(style))
}
@ -575,21 +589,18 @@ impl Window<IambInfo> for IambWindow {
fn find(name: String, store: &mut ProgramStore) -> IambResult<Self> {
let ChatStore { names, worker, .. } = &mut store.application;
match names.entry(name) {
Entry::Vacant(v) => {
let room_id = worker.join_room(v.key().to_string())?;
v.insert(room_id.clone());
if let Some(room) = names.get_mut(&name) {
let id = IambId::Room(room.clone());
let (room, name, tags) = store.application.worker.get_room(room_id)?;
let room = RoomState::new(room, name, tags, store);
IambWindow::open(id, store)
} else {
let room_id = worker.join_room(name.clone())?;
names.insert(name, room_id.clone());
Ok(room.into())
},
Entry::Occupied(o) => {
let id = IambId::Room(o.get().clone());
let (room, name, tags) = store.application.worker.get_room(room_id)?;
let room = RoomState::new(room, name, tags, store);
IambWindow::open(id, store)
},
Ok(room.into())
}
}
@ -620,11 +631,16 @@ impl RoomItem {
store: &mut ProgramStore,
) -> Self {
let name = name.to_string();
let room_id = room.room_id();
let info = store.application.get_room_info(room.room_id().to_owned());
let info = store.application.get_room_info(room_id.to_owned());
info.name = name.clone().into();
info.tags = tags.clone();
if let Some(alias) = room.canonical_alias() {
store.application.names.insert(alias.to_string(), room_id.to_owned());
}
RoomItem { room, tags, name }
}
}
@ -772,8 +788,13 @@ pub struct SpaceItem {
impl SpaceItem {
fn new(room: MatrixRoom, name: DisplayName, store: &mut ProgramStore) -> Self {
let name = name.to_string();
let room_id = room.room_id();
store.application.set_room_name(room.room_id(), name.as_str());
store.application.set_room_name(room_id, name.as_str());
if let Some(alias) = room.canonical_alias() {
store.application.names.insert(alias.to_string(), room_id.to_owned());
}
SpaceItem { room, name }
}

View file

@ -52,7 +52,8 @@ use modalkit::editing::{
Scrollable,
UIError,
},
base::{CloseFlags, Count, MoveDir1D, PositionList, ScrollStyle, WordStyle},
base::{CloseFlags, Count, MoveDir1D, PositionList, ScrollStyle, WordStyle, WriteFlags},
completion::CompletionList,
context::Resolve,
history::{self, HistoryList},
rope::EditRope,
@ -154,7 +155,7 @@ impl ChatState {
let client = &store.application.worker.client;
let settings = &store.application.settings;
let info = store.application.rooms.entry(self.room_id.clone()).or_default();
let info = store.application.rooms.get_or_default(self.room_id.clone());
let msg = self
.scrollback
@ -389,7 +390,7 @@ impl ChatState {
.client
.get_joined_room(self.id())
.ok_or(IambError::NotJoined)?;
let info = store.application.rooms.entry(self.id().to_owned()).or_default();
let info = store.application.rooms.get_or_default(self.id().to_owned());
let mut show_echo = true;
let (event_id, msg) = match act {
@ -550,6 +551,21 @@ impl WindowOps<IambInfo> for ChatState {
true
}
fn write(
&mut self,
_: Option<&str>,
_: WriteFlags,
_: &mut ProgramStore,
) -> IambResult<EditInfo> {
// XXX: what's the right writing behaviour for a room?
// Should write send a message?
Ok(None)
}
fn get_completions(&self) -> Option<CompletionList> {
delegate!(self, w => w.get_completions())
}
fn get_cursor_word(&self, style: &WordStyle) -> Option<String> {
delegate!(self, w => w.get_cursor_word(style))
}

View file

@ -40,7 +40,9 @@ use modalkit::{
PositionList,
ScrollStyle,
WordStyle,
WriteFlags,
},
editing::completion::CompletionList,
input::InputContext,
widgets::{TermOffset, TerminalCursor, WindowOps},
};
@ -383,10 +385,30 @@ impl WindowOps<IambInfo> for RoomState {
}
}
fn close(&mut self, _: CloseFlags, _: &mut ProgramStore) -> bool {
// XXX: what's the right closing behaviour for a room?
// Should write send a message?
true
fn close(&mut self, flags: CloseFlags, store: &mut ProgramStore) -> bool {
match self {
RoomState::Chat(chat) => chat.close(flags, store),
RoomState::Space(space) => space.close(flags, store),
}
}
fn write(
&mut self,
path: Option<&str>,
flags: WriteFlags,
store: &mut ProgramStore,
) -> IambResult<EditInfo> {
match self {
RoomState::Chat(chat) => chat.write(path, flags, store),
RoomState::Space(space) => space.write(path, flags, store),
}
}
fn get_completions(&self) -> Option<CompletionList> {
match self {
RoomState::Chat(chat) => chat.get_completions(),
RoomState::Space(space) => space.get_completions(),
}
}
fn get_cursor_word(&self, style: &WordStyle) -> Option<String> {

View file

@ -32,6 +32,9 @@ use modalkit::editing::{
base::{
Axis,
CloseFlags,
CompletionDisplay,
CompletionSelection,
CompletionType,
Count,
EditRange,
EditTarget,
@ -51,7 +54,9 @@ use modalkit::editing::{
TargetShape,
ViewportContext,
WordStyle,
WriteFlags,
},
completion::CompletionList,
context::{EditContext, Resolve},
cursor::{CursorGroup, CursorState},
history::HistoryList,
@ -60,7 +65,7 @@ use modalkit::editing::{
};
use crate::{
base::{IambBufferId, IambInfo, ProgramContext, ProgramStore, RoomFocus, RoomInfo},
base::{IambBufferId, IambInfo, IambResult, ProgramContext, ProgramStore, RoomFocus, RoomInfo},
config::ApplicationSettings,
message::{Message, MessageCursor, MessageKey, Messages},
};
@ -515,6 +520,23 @@ impl WindowOps<IambInfo> for ScrollbackState {
true
}
fn write(
&mut self,
_: Option<&str>,
flags: WriteFlags,
_: &mut ProgramStore,
) -> IambResult<EditInfo> {
if flags.contains(WriteFlags::FORCE) {
Ok(None)
} else {
Err(EditError::ReadOnly.into())
}
}
fn get_completions(&self) -> Option<CompletionList> {
None
}
fn get_cursor_word(&self, _: &WordStyle) -> Option<String> {
None
}
@ -532,7 +554,7 @@ impl EditorActions<ProgramContext, ProgramStore, IambInfo> for ScrollbackState {
ctx: &ProgramContext,
store: &mut ProgramStore,
) -> EditResult<EditInfo, IambInfo> {
let info = store.application.rooms.entry(self.room_id.clone()).or_default();
let info = store.application.rooms.get_or_default(self.room_id.clone());
let key = if let Some(k) = self.cursor.to_key(info) {
k.clone()
} else {
@ -762,6 +784,17 @@ impl EditorActions<ProgramContext, ProgramStore, IambInfo> for ScrollbackState {
}
}
fn complete(
&mut self,
_: &CompletionType,
_: &CompletionSelection,
_: &CompletionDisplay,
_: &ProgramContext,
_: &mut ProgramStore,
) -> EditResult<EditInfo, IambInfo> {
Err(EditError::ReadOnly)
}
fn insert_text(
&mut self,
_: &InsertTextAction,
@ -867,9 +900,9 @@ impl Editable<ProgramContext, ProgramStore, IambInfo> for ScrollbackState {
EditorAction::Mark(name) => self.mark(ctx.resolve(name), ctx, store),
EditorAction::Selection(act) => self.selection_command(act, ctx, store),
EditorAction::Complete(_, _) => {
let msg = "";
let err = EditError::Unimplemented(msg.into());
EditorAction::Complete(_, _, _) => {
let msg = "Nothing to complete in message scrollback";
let err = EditError::Failure(msg.into());
Err(err)
},
@ -991,7 +1024,7 @@ impl ScrollActions<ProgramContext, ProgramStore, IambInfo> for ScrollbackState {
ctx: &ProgramContext,
store: &mut ProgramStore,
) -> EditResult<EditInfo, IambInfo> {
let info = store.application.rooms.entry(self.room_id.clone()).or_default();
let info = store.application.rooms.get_or_default(self.room_id.clone());
let settings = &store.application.settings;
let mut corner = self.viewctx.corner.clone();
@ -1105,7 +1138,7 @@ impl ScrollActions<ProgramContext, ProgramStore, IambInfo> for ScrollbackState {
Err(err)
},
Axis::Vertical => {
let info = store.application.rooms.entry(self.room_id.clone()).or_default();
let info = store.application.rooms.get_or_default(self.room_id.clone());
let settings = &store.application.settings;
if let Some(key) = self.cursor.to_key(info).cloned() {
@ -1193,7 +1226,7 @@ impl<'a> StatefulWidget for Scrollback<'a> {
type State = ScrollbackState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let info = self.store.application.rooms.entry(state.room_id.clone()).or_default();
let info = self.store.application.rooms.get_or_default(state.room_id.clone());
let settings = &self.store.application.settings;
let area = info.render_typing(area, buf, &self.store.application.settings);

View file

@ -8,9 +8,11 @@ use modalkit::{
widgets::{TermOffset, TerminalCursor},
};
use modalkit::editing::base::{CloseFlags, WordStyle};
use modalkit::editing::action::EditInfo;
use modalkit::editing::base::{CloseFlags, WordStyle, WriteFlags};
use modalkit::editing::completion::CompletionList;
use crate::base::{IambBufferId, IambInfo, ProgramStore};
use crate::base::{IambBufferId, IambInfo, IambResult, ProgramStore};
const WELCOME_TEXT: &str = include_str!("welcome.md");
@ -63,6 +65,19 @@ impl WindowOps<IambInfo> for WelcomeState {
self.tbox.close(flags, store)
}
fn write(
&mut self,
path: Option<&str>,
flags: WriteFlags,
store: &mut ProgramStore,
) -> IambResult<EditInfo> {
self.tbox.write(path, flags, store)
}
fn get_completions(&self) -> Option<CompletionList> {
self.tbox.get_completions()
}
fn get_cursor_word(&self, style: &WordStyle) -> Option<String> {
self.tbox.get_cursor_word(style)
}