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:
parent
4bc2f736f2
commit
7e3919e25d
@ -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,27 +600,71 @@ 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
|
||||||
switch (event.keyCode) {
|
if (hasShortcutModifiers(event)) {
|
||||||
case 9:
|
switch (event.key) {
|
||||||
if (!event.ctrlKey && !event.metaKey && !event.altKey &&
|
case 'p':
|
||||||
!event.shiftKey)
|
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) {
|
||||||
|
case 9:
|
||||||
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()
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user