mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
synced 2024-11-25 14:56:20 +00:00
Edit summary autocomplete
Bug: T50274 Change-Id: Ida462f3dea325902459546af97e66cd9d0c86166
This commit is contained in:
parent
f501a5238b
commit
1ffb574450
|
@ -1469,6 +1469,7 @@
|
|||
"modules/ve-mw/ui/commands/ve.ui.MWWikitextWarningCommand.js",
|
||||
"modules/ve-mw/ui/datatransferhandlers/ve.ui.MWWikitextStringTransferHandler.js",
|
||||
"modules/ve-mw/ui/widgets/ve.ui.MWAceEditorWidget.js",
|
||||
"modules/ve-mw/ui/widgets/ve.ui.MWEditSummaryWidget.js",
|
||||
"modules/ve-mw/ui/widgets/ve.ui.MWLazyMultilineTextInputWidget.js",
|
||||
"modules/ve-mw/ui/widgets/ve.ui.MWTargetWidget.js",
|
||||
"modules/ve-mw/ui/widgets/ve.ui.MWTocWidget.js",
|
||||
|
|
|
@ -581,7 +581,8 @@ ve.ui.MWSaveDialog.prototype.initialize = function () {
|
|||
this.$editSummaryLabel = $( '<div>' ).addClass( 've-ui-mwSaveDialog-summaryLabel' )
|
||||
.html( ve.init.platform.getParsedMessage( 'summary' ) );
|
||||
ve.targetLinksToNewWindow( this.$editSummaryLabel[ 0 ] );
|
||||
this.editSummaryInput = new OO.ui.MultilineTextInputWidget( {
|
||||
this.editSummaryInput = new ve.ui.MWEditSummaryWidget( {
|
||||
$overlay: this.$overlay,
|
||||
placeholder: ve.msg( 'visualeditor-editsummary' ),
|
||||
classes: [ 've-ui-mwSaveDialog-summary' ],
|
||||
inputFilter: function ( value ) {
|
||||
|
|
172
modules/ve-mw/ui/widgets/ve.ui.MWEditSummaryWidget.js
Normal file
172
modules/ve-mw/ui/widgets/ve.ui.MWEditSummaryWidget.js
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*!
|
||||
* VisualEditor UserInterface MWEditSummaryWidget class.
|
||||
*
|
||||
* @copyright 2011-2018 VisualEditor Team and others; see http://ve.mit-license.org
|
||||
*/
|
||||
|
||||
/**
|
||||
* Multi line text input for edit summary, with auto completion based on
|
||||
* the user's previous edit summaries.
|
||||
*
|
||||
* @class
|
||||
* @extends OO.ui.MultilineTextInputWidget
|
||||
* @mixins OO.ui.mixin.LookupElement
|
||||
*
|
||||
* @constructor
|
||||
* @param {Object} [config] Configuration options
|
||||
* @cfg {number} [limit=6] Number of suggestions to show
|
||||
*/
|
||||
ve.ui.MWEditSummaryWidget = function VeUiMWEditSummaryWidget( config ) {
|
||||
config = config || {};
|
||||
|
||||
// Parent method
|
||||
ve.ui.MWEditSummaryWidget.super.apply( this, arguments );
|
||||
|
||||
// Mixin method
|
||||
OO.ui.mixin.LookupElement.call( this, ve.extendObject( { allowSuggestionsWhenEmpty: false }, config ) );
|
||||
|
||||
this.limit = config.limit || 6;
|
||||
};
|
||||
|
||||
/* Inheritance */
|
||||
|
||||
OO.inheritClass( ve.ui.MWEditSummaryWidget, OO.ui.MultilineTextInputWidget );
|
||||
|
||||
OO.mixinClass( ve.ui.MWEditSummaryWidget, OO.ui.mixin.LookupElement );
|
||||
|
||||
/* Static properties */
|
||||
|
||||
ve.ui.MWEditSummaryWidget.static.summarySplitter = /^(\/\*.*?\*\/\s*)?(.*)$/;
|
||||
|
||||
/* Static methods */
|
||||
|
||||
/**
|
||||
* Split a summary into the section and the actual summary
|
||||
* @param {string} summary Summary
|
||||
* @return {Object} Object with section and comment string properties
|
||||
*/
|
||||
ve.ui.MWEditSummaryWidget.static.splitSummary = function ( summary ) {
|
||||
var result = summary.match( this.summarySplitter );
|
||||
return {
|
||||
section: result[ 1 ] || '',
|
||||
comment: result[ 2 ]
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter a list of edit summaries to a specific query stirng
|
||||
*
|
||||
* @param {string[]} summaries Edit summaries
|
||||
* @param {string} query User query
|
||||
* @return {string[]} Filtered edit summaries
|
||||
*/
|
||||
ve.ui.MWEditSummaryWidget.static.getMatchingSummaries = function ( summaries, query ) {
|
||||
var summaryPrefixMatches = [], wordPrefixMatches = [], otherMatches = [],
|
||||
lowerQuery = query.toLowerCase();
|
||||
|
||||
if ( !query.trim() ) {
|
||||
// Show no results for empty query
|
||||
return [];
|
||||
}
|
||||
|
||||
summaries.forEach( function ( summary ) {
|
||||
var lowerSummary = summary.toLowerCase(),
|
||||
index = lowerSummary.indexOf( lowerQuery );
|
||||
if ( index === 0 ) {
|
||||
// Exclude exact matches
|
||||
if ( lowerQuery !== summary ) {
|
||||
summaryPrefixMatches.push( summary );
|
||||
}
|
||||
} else if ( index !== -1 ) {
|
||||
if ( lowerSummary[ index - 1 ].match( /\s/ ) ) {
|
||||
// Character before match is whitespace
|
||||
wordPrefixMatches.push( summary );
|
||||
} else {
|
||||
otherMatches.push( summary );
|
||||
}
|
||||
}
|
||||
} );
|
||||
return summaryPrefixMatches.concat( wordPrefixMatches, otherMatches );
|
||||
};
|
||||
|
||||
/* Methods */
|
||||
|
||||
/**
|
||||
* Get recent edit summaries for the logged in user
|
||||
*
|
||||
* @return {jQuery.Promise} Promise which resolves with a list of summaries
|
||||
*/
|
||||
ve.ui.MWEditSummaryWidget.prototype.getSummaries = function () {
|
||||
var splitSummary = this.constructor.static.splitSummary.bind( this.constructor.static );
|
||||
if ( !this.getSummariesPromise ) {
|
||||
if ( mw.user.isAnon() ) {
|
||||
this.getSummariesPromise = $.Deferred().resolve( [] ).promise();
|
||||
} else {
|
||||
this.getSummariesPromise = new mw.Api().get( {
|
||||
action: 'query',
|
||||
list: 'usercontribs',
|
||||
ucuser: mw.user.getName(),
|
||||
ucprop: 'comment|title',
|
||||
uclimit: 500,
|
||||
format: 'json',
|
||||
formatversion: 2
|
||||
} ).then( function ( response ) {
|
||||
var usedComments = {},
|
||||
changes = ve.getProp( response, 'query', 'usercontribs' ) || [];
|
||||
|
||||
return changes
|
||||
// Remove section /* headings */
|
||||
.map( function ( change ) {
|
||||
return splitSummary( change.comment ).comment.trim();
|
||||
} )
|
||||
// Filter out duplicates and empty comments
|
||||
.filter( function ( comment ) {
|
||||
if ( !comment || Object.prototype.hasOwnProperty.call( usedComments, comment ) ) {
|
||||
return false;
|
||||
}
|
||||
usedComments[ comment ] = true;
|
||||
return true;
|
||||
} )
|
||||
.sort();
|
||||
} );
|
||||
}
|
||||
}
|
||||
return this.getSummariesPromise;
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ve.ui.MWEditSummaryWidget.prototype.getLookupRequest = function () {
|
||||
var query = this.constructor.static.splitSummary( this.value ),
|
||||
limit = this.limit,
|
||||
widget = this;
|
||||
|
||||
return this.getSummaries().then( function ( allSummaries ) {
|
||||
var matchingSummaries = widget.constructor.static.getMatchingSummaries( allSummaries, query.comment );
|
||||
if ( matchingSummaries.length > limit ) {
|
||||
// Quick in-place truncate
|
||||
matchingSummaries.length = limit;
|
||||
}
|
||||
return { summaries: matchingSummaries, section: query.section };
|
||||
} ).promise( { abort: function () {} } ); // don't abort, the actual request will be the same anyway
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ve.ui.MWEditSummaryWidget.prototype.getLookupCacheDataFromResponse = function ( response ) {
|
||||
return response;
|
||||
};
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ve.ui.MWEditSummaryWidget.prototype.getLookupMenuOptionsFromData = function ( data ) {
|
||||
return data.summaries.map( function ( item ) {
|
||||
return new OO.ui.MenuOptionWidget( {
|
||||
label: item,
|
||||
data: data.section + item
|
||||
} );
|
||||
} );
|
||||
};
|
Loading…
Reference in a new issue