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 {
|
return {
|
||||||
connect: connect,
|
connect: connect,
|
||||||
@ -538,7 +557,8 @@ weechat.factory('connection',
|
|||||||
sendHotlistClearAll: sendHotlistClearAll,
|
sendHotlistClearAll: sendHotlistClearAll,
|
||||||
fetchMoreLines: fetchMoreLines,
|
fetchMoreLines: fetchMoreLines,
|
||||||
requestNicklist: requestNicklist,
|
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 = {
|
var eventHandlers = {
|
||||||
_buffer_closing: handleBufferClosing,
|
_buffer_closing: handleBufferClosing,
|
||||||
_buffer_line_added: handleBufferLineAdded,
|
_buffer_line_added: handleBufferLineAdded,
|
||||||
@ -529,7 +535,8 @@ weechat.factory('handlers', ['$rootScope', '$log', 'models', 'plugins', 'notific
|
|||||||
handleLineInfo: handleLineInfo,
|
handleLineInfo: handleLineInfo,
|
||||||
handleHotlistInfo: handleHotlistInfo,
|
handleHotlistInfo: handleHotlistInfo,
|
||||||
handleNicklist: handleNicklist,
|
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
|
// 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)
|
// don't do replacements in the middle of a word (e.g. std::io::foo)
|
||||||
$scope.inputChanged = function() {
|
$scope.inputChanged = function() {
|
||||||
|
// Cancel any command completion that was still ongoing
|
||||||
|
commandCompletionInputChanged = true;
|
||||||
|
|
||||||
var emojiRegex = /^(?:[\uD800-\uDBFF][\uDC00-\uDFFF])+$/, // *only* emoji
|
var emojiRegex = /^(?:[\uD800-\uDBFF][\uDC00-\uDFFF])+$/, // *only* emoji
|
||||||
changed = false, // whether a segment was modified
|
changed = false, // whether a segment was modified
|
||||||
inputNode = $scope.getInputNode(),
|
inputNode = $scope.getInputNode(),
|
||||||
@ -76,6 +79,13 @@ weechat.directive('inputBar', function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
$scope.completeNick = 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
|
// input DOM node
|
||||||
var inputNode = $scope.getInputNode();
|
var inputNode = $scope.getInputNode();
|
||||||
|
|
||||||
@ -108,6 +118,124 @@ weechat.directive('inputBar', function() {
|
|||||||
}, 0);
|
}, 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) {
|
$rootScope.insertAtCaret = function(toInsert) {
|
||||||
// caret position in the input bar
|
// caret position in the input bar
|
||||||
var inputNode = $scope.getInputNode(),
|
var inputNode = $scope.getInputNode(),
|
||||||
@ -367,10 +495,18 @@ weechat.directive('inputBar', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tab -> nick completion
|
// Tab -> nick completion
|
||||||
if (code === 9 && !$event.altKey && !$event.ctrlKey) {
|
if (code === 9 && !$event.altKey && !$event.ctrlKey && !$event.shiftKey) {
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
$scope.iterCandidate = tmpIterCandidate;
|
$scope.iterCandidate = tmpIterCandidate;
|
||||||
$scope.completeNick();
|
$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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -606,6 +742,7 @@ weechat.directive('inputBar', function() {
|
|||||||
$scope.handleCompleteNickButton = function($event) {
|
$scope.handleCompleteNickButton = function($event) {
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
$scope.completeNick();
|
$scope.completeNick();
|
||||||
|
$scope.completeCommand('forward');
|
||||||
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
$scope.getInputNode().focus();
|
$scope.getInputNode().focus();
|
||||||
|
@ -777,6 +777,33 @@
|
|||||||
return WeeChatProtocol._formatCmd(params.id, 'input', parts);
|
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.
|
* Formats a sync or a desync command.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user