xP: add basic buffer input history

Bind M-p and M-n as in xC.

Also make all our bindings reachable on macOS.
This commit is contained in:
Přemysl Eric Janouch 2022-09-11 02:54:39 +02:00
parent 4bc2f736f2
commit 7e3919e25d
Signed by: p
GPG Key ID: A0420B94F92B9493

View File

@ -197,7 +197,11 @@ rpc.addEventListener('Ping', event => {
rpc.addEventListener('BufferUpdate', event => { rpc.addEventListener('BufferUpdate', event => {
let e = event.detail, b = buffers.get(e.bufferName) let e = event.detail, b = buffers.get(e.bufferName)
if (b === undefined) { if (b === undefined) {
buffers.set(e.bufferName, (b = {lines: []})) buffers.set(e.bufferName, (b = {
lines: [],
history: [],
historyAt: 0,
}))
bufferResetStats(b) bufferResetStats(b)
} }
b.hideUnimportant = e.hideUnimportant b.hideUnimportant = e.hideUnimportant
@ -247,6 +251,9 @@ rpc.addEventListener('BufferActivate', event => {
old.inputStart = textarea.selectionStart old.inputStart = textarea.selectionStart
old.inputEnd = textarea.selectionEnd old.inputEnd = textarea.selectionEnd
old.inputDirection = textarea.selectionDirection old.inputDirection = textarea.selectionDirection
// Note that we effectively overwrite the newest line
// with the current textarea contents, and jump there.
old.historyAt = old.history.length
} }
textarea.value = '' textarea.value = ''
@ -324,6 +331,15 @@ for (let i = 0; i < 24; i++) {
// ---- UI --------------------------------------------------------------------- // ---- UI ---------------------------------------------------------------------
// On macOS, the Alt/Option key transforms characters, which basically breaks
// all event.altKey shortcuts, so require combining them with Control as well
// on that system.
function hasShortcutModifiers(event) {
// This method of detection only works with Blink browsers, as of writing.
return event.altKey && !event.metaKey &&
(navigator.userAgentData?.platform === 'macOS') === event.ctrlKey
}
let linkRE = [ let linkRE = [
/https?:\/\//, /https?:\/\//,
/([^\[\](){}<>"'\s]|\([^\[\](){}<>"'\s]*\))+/, /([^\[\](){}<>"'\s]|\([^\[\](){}<>"'\s]*\))+/,
@ -584,28 +600,72 @@ let Input = {
bufferName: bufferCurrent, bufferName: bufferCurrent,
text: textarea.value, text: textarea.value,
}) })
// b.history[b.history.length] is virtual, and is represented
// either by textarea contents when it's currently being edited,
// or by b.input in all other cases.
let b = buffers.get(bufferCurrent)
b.history.push(textarea.value)
b.historyAt = b.history.length
textarea.value = '' textarea.value = ''
return true return true
}, },
previous: textarea => {
let b = buffers.get(bufferCurrent)
if (b === undefined)
return false
// TODO: Ding otherwise.
if (b.historyAt > 0) {
if (b.historyAt == b.history.length)
b.input = textarea.value
textarea.value = b.history[--b.historyAt]
}
return true
},
next: textarea => {
let b = buffers.get(bufferCurrent)
if (b === undefined)
return false
// TODO: Ding otherwise.
if (b.historyAt < b.history.length) {
if (++b.historyAt == b.history.length)
textarea.value = b.input
else
textarea.value = b.history[b.historyAt]
}
return true
},
onKeyDown: event => { onKeyDown: event => {
// TODO: And perhaps on other actions, too. // TODO: And perhaps on other actions, too.
rpc.send({command: 'Active'}) rpc.send({command: 'Active'})
let textarea = event.currentTarget let textarea = event.currentTarget
let handled = false let handled = false
if (hasShortcutModifiers(event)) {
switch (event.key) {
case 'p':
handled = Input.previous(textarea)
break
case 'n':
handled = Input.next(textarea)
break
}
} else if (!event.altKey && !event.ctrlKey && !event.metaKey &&
!event.shiftKey) {
switch (event.keyCode) { switch (event.keyCode) {
case 9: case 9:
if (!event.ctrlKey && !event.metaKey && !event.altKey &&
!event.shiftKey)
handled = Input.complete(textarea) handled = Input.complete(textarea)
break break
case 13: case 13:
if (!event.ctrlKey && !event.metaKey && !event.altKey &&
!event.shiftKey)
handled = Input.submit(textarea) handled = Input.submit(textarea)
break break
} }
}
if (handled) if (handled)
event.preventDefault() event.preventDefault()
}, },
@ -636,28 +696,34 @@ let Main = {
window.addEventListener('load', () => m.mount(document.body, Main)) window.addEventListener('load', () => m.mount(document.body, Main))
document.addEventListener('keydown', event => { document.addEventListener('keydown', event => {
if (rpc.ws == undefined || event.ctrlKey || event.metaKey) if (rpc.ws == undefined || !hasShortcutModifiers(event))
return return
if (event.altKey && event.key == 'Tab') { switch (event.key) {
case 'Tab':
if (bufferLast !== undefined) if (bufferLast !== undefined)
bufferActivate(bufferLast) bufferActivate(bufferLast)
} else if (event.altKey && event.key == 'h') { break
case 'h':
bufferToggleLog() bufferToggleLog()
} else if (event.altKey && event.key == 'a') { break
case 'a':
for (const [name, b] of buffers) for (const [name, b] of buffers)
if (name !== bufferCurrent && b.newMessages) { if (name !== bufferCurrent && b.newMessages) {
bufferActivate(name) bufferActivate(name)
break break
} }
} else if (event.altKey && event.key == '!') { break
case '!':
for (const [name, b] of buffers) for (const [name, b] of buffers)
if (name !== bufferCurrent && b.highlighted) { if (name !== bufferCurrent && b.highlighted) {
bufferActivate(name) bufferActivate(name)
break break
} }
} else break
default:
return return
}
event.preventDefault() event.preventDefault()
}) })