Merge pull request #1122 from AStove/CommandCompletion
Command completion for weechat 2.9
This commit is contained in:
commit
126d7dc67f
|
@ -528,6 +528,25 @@ weechat.factory('connection',
|
|||
});
|
||||
};
|
||||
|
||||
var requestCompletion = function(bufferId, position, data) {
|
||||
// Prevent requesting completion if bufferId is invalid
|
||||
if (!bufferId) {
|
||||
return;
|
||||
}
|
||||
|
||||
return ngWebsockets.send(
|
||||
weeChat.Protocol.formatCompletion({
|
||||
buffer: "0x" + bufferId,
|
||||
position: position,
|
||||
data: data
|
||||
})
|
||||
).then(function(message) {
|
||||
return new Promise(function (resolve) {
|
||||
resolve( handlers.handleCompletion(message) );
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
connect: connect,
|
||||
|
@ -538,7 +557,8 @@ weechat.factory('connection',
|
|||
sendHotlistClearAll: sendHotlistClearAll,
|
||||
fetchMoreLines: fetchMoreLines,
|
||||
requestNicklist: requestNicklist,
|
||||
attemptReconnect: attemptReconnect
|
||||
attemptReconnect: attemptReconnect,
|
||||
requestCompletion: requestCompletion
|
||||
};
|
||||
}]);
|
||||
})();
|
||||
|
|
|
@ -491,6 +491,12 @@ weechat.factory('handlers', ['$rootScope', '$log', 'models', 'plugins', 'notific
|
|||
});
|
||||
};
|
||||
|
||||
var handleCompletion = function(message) {
|
||||
var completionInfo = message.objects[0].content[0];
|
||||
|
||||
return completionInfo;
|
||||
};
|
||||
|
||||
var eventHandlers = {
|
||||
_buffer_closing: handleBufferClosing,
|
||||
_buffer_line_added: handleBufferLineAdded,
|
||||
|
@ -529,7 +535,8 @@ weechat.factory('handlers', ['$rootScope', '$log', 'models', 'plugins', 'notific
|
|||
handleLineInfo: handleLineInfo,
|
||||
handleHotlistInfo: handleHotlistInfo,
|
||||
handleNicklist: handleNicklist,
|
||||
handleBufferInfo: handleBufferInfo
|
||||
handleBufferInfo: handleBufferInfo,
|
||||
handleCompletion: handleCompletion
|
||||
};
|
||||
|
||||
}]);
|
||||
|
|
139
js/inputbar.js
139
js/inputbar.js
|
@ -31,6 +31,9 @@ weechat.directive('inputBar', function() {
|
|||
// Emojify input. E.g. Turn :smile: into the unicode equivalent, but
|
||||
// don't do replacements in the middle of a word (e.g. std::io::foo)
|
||||
$scope.inputChanged = function() {
|
||||
// Cancel any command completion that was still ongoing
|
||||
commandCompletionInputChanged = true;
|
||||
|
||||
var emojiRegex = /^(?:[\uD800-\uDBFF][\uDC00-\uDFFF])+$/, // *only* emoji
|
||||
changed = false, // whether a segment was modified
|
||||
inputNode = $scope.getInputNode(),
|
||||
|
@ -76,6 +79,13 @@ weechat.directive('inputBar', function() {
|
|||
};
|
||||
|
||||
$scope.completeNick = function() {
|
||||
if ((models.version[0] == 2 && models.version[1] >= 9 || models.version[0] > 2) &&
|
||||
$scope.command.startsWith('/') ) {
|
||||
// We are completing a command, another function will do
|
||||
// this on WeeChat 2.9 and later
|
||||
return;
|
||||
}
|
||||
|
||||
// input DOM node
|
||||
var inputNode = $scope.getInputNode();
|
||||
|
||||
|
@ -108,6 +118,124 @@ weechat.directive('inputBar', function() {
|
|||
}, 0);
|
||||
};
|
||||
|
||||
var previousInput;
|
||||
var commandCompletionList;
|
||||
var commandCompletionAddSpace;
|
||||
var commandCompletionBaseWord;
|
||||
var commandCompletionPosition;
|
||||
var commandCompletionPositionInList;
|
||||
var commandCompletionInputChanged;
|
||||
$scope.completeCommand = function(direction) {
|
||||
if (models.version[0] < 2 || (models.version[0] == 2 && models.version[1] < 9)) {
|
||||
// Command completion is only supported on WeeChat 2.9+
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !$scope.command.startsWith('/') ) {
|
||||
// We are not completing a command, maybe a nick?
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel if input changes
|
||||
commandCompletionInputChanged = false;
|
||||
|
||||
// input DOM node
|
||||
var inputNode = $scope.getInputNode();
|
||||
|
||||
// get current caret position
|
||||
var caretPos = inputNode.selectionStart;
|
||||
|
||||
// get current active buffer
|
||||
var activeBuffer = models.getActiveBuffer();
|
||||
|
||||
// Empty input makes $scope.command undefined -- use empty string instead
|
||||
var input = $scope.command || '';
|
||||
|
||||
// This function is for later cycling the list after we got it
|
||||
var cycleCompletionList = function (direction) {
|
||||
// Don't do anything, the input has changed before we were able to complete the command
|
||||
if ( commandCompletionInputChanged ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the list has elements and we have not cycled to the end yet
|
||||
if ( !commandCompletionList || !commandCompletionList[0] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are cycling in the other direction, go back two placed in the list
|
||||
if ( direction === 'backward' ) {
|
||||
commandCompletionPositionInList -= 2;
|
||||
|
||||
if ( commandCompletionPositionInList < 0 ) {
|
||||
// We have reached the beginning of list and are going backward, so go to the end;
|
||||
commandCompletionPositionInList = commandCompletionList.length - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Check we have not reached the end of the cycle list
|
||||
if ( commandCompletionList.length <= commandCompletionPositionInList ) {
|
||||
// We have reached the end of the list, start at the beginning
|
||||
commandCompletionPositionInList = 0;
|
||||
}
|
||||
|
||||
// Cycle the list
|
||||
// First remove the word that's to be completed
|
||||
var commandBeforeReplace = $scope.command.substring(0, commandCompletionPosition - commandCompletionBaseWord.length);
|
||||
var commandAfterReplace = $scope.command.substring(commandCompletionPosition, $scope.command.length);
|
||||
var replacedWord = commandCompletionList[commandCompletionPositionInList];
|
||||
var suffix = commandCompletionAddSpace ? ' ' : '';
|
||||
|
||||
// Fill in the new command
|
||||
$scope.command = commandBeforeReplace + replacedWord + suffix + commandAfterReplace;
|
||||
|
||||
// Set the cursor position
|
||||
var newCursorPos = commandBeforeReplace.length + replacedWord.length + suffix.length;
|
||||
setTimeout(function() {
|
||||
inputNode.focus();
|
||||
inputNode.setSelectionRange(newCursorPos, newCursorPos);
|
||||
}, 0);
|
||||
|
||||
// If there is only one item in the list, we are done, no next cycle
|
||||
if ( commandCompletionList.length === 1) {
|
||||
previousInput = '';
|
||||
return;
|
||||
}
|
||||
// Setup for the next cycle
|
||||
commandCompletionPositionInList++;
|
||||
commandCompletionBaseWord = replacedWord + suffix;
|
||||
previousInput = $scope.command + activeBuffer.id;
|
||||
commandCompletionPosition = newCursorPos;
|
||||
}
|
||||
|
||||
// Check if we have requested this completion info before
|
||||
if (input + activeBuffer.id !== previousInput) {
|
||||
// Remeber we requested this input for next time
|
||||
previousInput = input + activeBuffer.id;
|
||||
|
||||
// Ask weechat for the completion list
|
||||
connection.requestCompletion(activeBuffer.id, caretPos, input).then( function(completionObject) {
|
||||
// Save the list of completion object, we will only request is once
|
||||
// and cycle through it as long as the input doesn't change
|
||||
commandCompletionList = completionObject.list;
|
||||
commandCompletionAddSpace = completionObject.add_space
|
||||
commandCompletionBaseWord = completionObject.base_word;
|
||||
commandCompletionPosition = caretPos;
|
||||
commandCompletionPositionInList = 0;
|
||||
}).then( function () {
|
||||
//after we get the list we can continue with our first cycle
|
||||
cycleCompletionList(direction);
|
||||
});
|
||||
|
||||
|
||||
} else {
|
||||
// Input hasn't changed so we should already have our completion list
|
||||
cycleCompletionList(direction);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
$rootScope.insertAtCaret = function(toInsert) {
|
||||
// caret position in the input bar
|
||||
var inputNode = $scope.getInputNode(),
|
||||
|
@ -367,10 +495,18 @@ weechat.directive('inputBar', function() {
|
|||
}
|
||||
|
||||
// Tab -> nick completion
|
||||
if (code === 9 && !$event.altKey && !$event.ctrlKey) {
|
||||
if (code === 9 && !$event.altKey && !$event.ctrlKey && !$event.shiftKey) {
|
||||
$event.preventDefault();
|
||||
$scope.iterCandidate = tmpIterCandidate;
|
||||
$scope.completeNick();
|
||||
$scope.completeCommand('forward');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Shitft-Tab -> nick completion backward (only commands)
|
||||
if (code === 9 && !$event.altKey && !$event.ctrlKey && $event.shiftKey) {
|
||||
$event.preventDefault();
|
||||
$scope.completeCommand('backward');
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -606,6 +742,7 @@ weechat.directive('inputBar', function() {
|
|||
$scope.handleCompleteNickButton = function($event) {
|
||||
$event.preventDefault();
|
||||
$scope.completeNick();
|
||||
$scope.completeCommand('forward');
|
||||
|
||||
setTimeout(function() {
|
||||
$scope.getInputNode().focus();
|
||||
|
|
|
@ -777,6 +777,33 @@
|
|||
return WeeChatProtocol._formatCmd(params.id, 'input', parts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats a completion command.
|
||||
* https://weechat.org/files/doc/stable/weechat_relay_protocol.en.html#command_completion
|
||||
* @param params Parameters:
|
||||
* id: command ID (optional)
|
||||
* buffer: target buffer (mandatory)
|
||||
* position: position for completion in string (optional)
|
||||
* data: input data (optional)
|
||||
* @return Formatted input command string
|
||||
*/
|
||||
WeeChatProtocol.formatCompletion = function(params) {
|
||||
var defaultParams = {
|
||||
id: null,
|
||||
position: -1
|
||||
};
|
||||
var parts = [];
|
||||
|
||||
params = WeeChatProtocol._mergeParams(defaultParams, params);
|
||||
parts.push(params.buffer);
|
||||
parts.push(params.position);
|
||||
if (params.data) {
|
||||
parts.push(params.data);
|
||||
}
|
||||
|
||||
return WeeChatProtocol._formatCmd(params.id, 'completion', parts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats a sync or a desync command.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue