diff --git a/components/app.js b/components/app.js index cdd50e9..70011f2 100644 --- a/components/app.js +++ b/components/app.js @@ -55,6 +55,7 @@ export default class App extends Component { this.handleComposerSubmit = this.handleComposerSubmit.bind(this); this.handleNickClick = this.handleNickClick.bind(this); this.handleJoinClick = this.handleJoinClick.bind(this); + this.autocomplete = this.autocomplete.bind(this); if (window.localStorage && localStorage.getItem("autoconnect")) { var connectParams = JSON.parse(localStorage.getItem("autoconnect")); @@ -554,6 +555,26 @@ export default class App extends Component { this.client.send({ command: "JOIN", params: [channel] }); } + autocomplete(prefix) { + if (!this.state.activeBuffer) { + return null; + } + var buf = this.state.buffers.get(this.state.activeBuffer); + + prefix = prefix.toLowerCase(); + + var repl = null; + for (var nick of buf.members.keys()) { + if (nick.toLowerCase().startsWith(prefix)) { + if (repl) { + return null; + } + repl = nick; + } + } + return repl; + } + componentDidMount() { if (this.state.connectParams.autoconnect) { this.connect(this.state.connectParams); @@ -609,7 +630,7 @@ export default class App extends Component { > ${memberList} - <${Composer} ref=${this.composer} readOnly=${this.state.activeBuffer == SERVER_BUFFER} onSubmit=${this.handleComposerSubmit}/> + <${Composer} ref=${this.composer} readOnly=${this.state.activeBuffer == SERVER_BUFFER} onSubmit=${this.handleComposerSubmit} autocomplete=${this.autocomplete}/> `; } } diff --git a/components/composer.js b/components/composer.js index eacc483..7d581a2 100644 --- a/components/composer.js +++ b/components/composer.js @@ -11,6 +11,7 @@ export default class Composer extends Component { this.handleInput = this.handleInput.bind(this); this.handleSubmit = this.handleSubmit.bind(this); + this.handleInputKeyDown = this.handleInputKeyDown.bind(this); this.handleWindowKeyDown = this.handleWindowKeyDown.bind(this); } @@ -24,8 +25,36 @@ export default class Composer extends Component { this.setState({ text: "" }); } + handleInputKeyDown(event) { + if (!this.props.autocomplete || event.key !== "Tab") { + return; + } + + var text = this.state.text; + var i; + for (i = text.length - 1; i >= 0; i--) { + if (text[i] === " ") { + break; + } + } + var prefix = text.slice(i + 1); + if (!prefix) { + return; + } + + event.preventDefault(); + + var repl = this.props.autocomplete(prefix); + if (!repl) { + return; + } + + text = text.slice(0, i + 1) + repl; + this.setState({ text }); + } + handleWindowKeyDown(event) { - if (document.activeElement == document.body && event.key == "/" && !this.state.text) { + if (document.activeElement === document.body && event.key === "/" && !this.state.text) { event.preventDefault(); this.setState({ text: "/" }, () => { this.focus(); @@ -49,7 +78,7 @@ export default class Composer extends Component { render() { return html`
`; }