mirror of
https://github.com/xevidos/codiad.git
synced 2024-12-22 05:42:17 +01:00
Removed autocomplete in favor for ace\'s built in autocomplete. Added verbose option to autosave console messages.
This commit is contained in:
parent
db5acdd4df
commit
0408ad74b3
6 changed files with 64 additions and 704 deletions
|
@ -1,696 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) Codiad & Kent Safranski (codiad.com), distributed
|
||||
* as-is and without warranty under the MIT License. See
|
||||
* [root]/license.txt for more. This information must remain intact.
|
||||
*/
|
||||
|
||||
(function (global, $) {
|
||||
|
||||
var EventEmitter = ace.require('ace/lib/event_emitter').EventEmitter;
|
||||
var Range = ace.require('ace/range').Range;
|
||||
|
||||
|
||||
var codiad = global.codiad;
|
||||
|
||||
$(function () {
|
||||
codiad.autocomplete.init();
|
||||
});
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Autocomplete Component for Codiad
|
||||
// ---------------------------------
|
||||
// Show a popup with word completion suggestions.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
codiad.autocomplete = {
|
||||
|
||||
wordRegex: /[^a-zA-Z_0-9\$]+/,
|
||||
|
||||
isVisible: false,
|
||||
|
||||
standardGoLineDownExec: null,
|
||||
|
||||
standardGoLineUpExec: null,
|
||||
|
||||
_suggestionCache: null,
|
||||
|
||||
standardGoToRightExec: null,
|
||||
|
||||
standardGoToLeftExec: null,
|
||||
|
||||
standardIndentExec: null,
|
||||
|
||||
init: function () {
|
||||
var _this = this;
|
||||
|
||||
this.$onDocumentChange = this.onDocumentChange.bind(this);
|
||||
this.$selectNextSuggestion = this.selectNextSuggestion.bind(this);
|
||||
this.$selectPreviousSuggestion = this.selectPreviousSuggestion.bind(this);
|
||||
this.$complete = this.complete.bind(this);
|
||||
this.$hide = this.hide.bind(this);
|
||||
|
||||
/* Catch click on suggestion */
|
||||
$('#autocomplete li').live('click', function () {
|
||||
$('#autocomplete li.active-suggestion').removeClass('active-suggestion');
|
||||
$(this).addClass('active-suggestion');
|
||||
_this.complete();
|
||||
});
|
||||
|
||||
/* In debug mode, run some tests here. */
|
||||
// this._testSimpleMatchScorer();
|
||||
// this._testFuzzyMatcher();
|
||||
},
|
||||
|
||||
suggest: function () {
|
||||
var _this = this;
|
||||
|
||||
var cursorPosition = this._getEditor().getCursorPosition();
|
||||
var foundSuggestions = this.updateSuggestions(cursorPosition);
|
||||
if (foundSuggestions) {
|
||||
this.addListenerToOnDocumentChange();
|
||||
|
||||
// Show the completion popup.
|
||||
this.show();
|
||||
|
||||
// handle click-out autoclosing.
|
||||
var fn = function ( event ) {
|
||||
let keycodes = [ 9, 10, 13 ]
|
||||
|
||||
/*if( ! keycodes.includes( event.keyCode ) ) {
|
||||
return;
|
||||
}*/
|
||||
_this.hide();
|
||||
$(window).off('click', fn);
|
||||
};
|
||||
$(window).on('click', fn);
|
||||
} else {
|
||||
this.clearSuggestionCache();
|
||||
}
|
||||
},
|
||||
|
||||
/* Update the suggestions for the word at the given position. Return true if
|
||||
* some suitable suggestions could be found, false if not. */
|
||||
updateSuggestions: function (position) {
|
||||
var _this = this;
|
||||
|
||||
var session = this._getEditSession();
|
||||
|
||||
/* Extract the word being typed. Keep only the part of the word
|
||||
* which is before the given position. It is somehow the prefix of
|
||||
* the wanted full word. Make sure we only keep one word. */
|
||||
var token = session.getTokenAt(position.row, position.column);
|
||||
if (!token) {
|
||||
/* No word at the given position. */
|
||||
this.removeSuggestions();
|
||||
return false;
|
||||
}
|
||||
|
||||
var prefix = token.value.substr(0, position.column - token.start);
|
||||
prefix = prefix.split(this.wordRegex).slice(-1)[0];
|
||||
if (prefix === '') {
|
||||
/* No word at the given position. */
|
||||
this.removeSuggestions();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Build and order the suggestions themselves. */
|
||||
// TODO cache suggestions and augment them incrementally.
|
||||
var suggestionsAndDistance = this.getSuggestions(position);
|
||||
var suggestions = this.rankSuggestions(prefix, suggestionsAndDistance);
|
||||
if (suggestions.length < 1) {
|
||||
/* No suitable suggestions found. */
|
||||
this.removeSuggestions();
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Remove the existing suggestions and populate the popup with the
|
||||
* updated ones. */
|
||||
this.removeSuggestions();
|
||||
var popupContent = $('#autocomplete #suggestions');
|
||||
$.each(suggestions, function (index, suggestion) {
|
||||
/* First get rid of the suggestion suffix. */
|
||||
suggestion = suggestion.slice(0, -1);
|
||||
|
||||
var indexes = _this.getMatchIndexes(prefix, suggestion);
|
||||
$.each(indexes.reverse(), function (index, matchIndex) {
|
||||
suggestion = suggestion.substr(0, matchIndex) +
|
||||
'<span class="matched">' +
|
||||
suggestion.substr(matchIndex, 1) +
|
||||
'</span>' +
|
||||
suggestion.substr(matchIndex + 1);
|
||||
});
|
||||
popupContent.append('<li class="suggestion">' + suggestion + '</li>');
|
||||
});
|
||||
|
||||
this.selectFirstSuggestion();
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
show: function () {
|
||||
this.isVisible = true;
|
||||
|
||||
var popup = $('#autocomplete');
|
||||
popup.css({
|
||||
'top': this._computeTopOffset(),
|
||||
'left': this._computeLeftOffset(),
|
||||
'font-family': $('.ace_editor').css('font-family'),
|
||||
'font-size': $('.ace_editor').css('font-size')
|
||||
});
|
||||
popup.slideToggle('fast', function(){
|
||||
$(this).css('overflow', '');
|
||||
});
|
||||
|
||||
this.addKeyboardCommands();
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
this.isVisible = false;
|
||||
|
||||
$('#autocomplete').hide();
|
||||
this.removeSuggestions();
|
||||
this.clearSuggestionCache();
|
||||
|
||||
this.removeListenerToOnDocumentChange();
|
||||
this.removeKeyboardCommands();
|
||||
},
|
||||
|
||||
/* Return a jQuery object containing the currently selected suggestion. */
|
||||
getSelectedSuggestion: function () {
|
||||
var selectedSuggestion = $('#autocomplete li.suggestion.active-suggestion');
|
||||
|
||||
if (selectedSuggestion.length < 1) {
|
||||
alert(i18n('No suggestion selected. Might be a bug.'));
|
||||
} else if (selectedSuggestion.length > 1) {
|
||||
alert(i18n('More than one suggestions selected. Might be a bug.'));
|
||||
}
|
||||
|
||||
return selectedSuggestion;
|
||||
},
|
||||
|
||||
selectFirstSuggestion: function () {
|
||||
var firstChild = $('li.suggestion:first-child');
|
||||
firstChild.addClass('active-suggestion');
|
||||
this._ensureVisible(firstChild, $('#autocomplete'));
|
||||
},
|
||||
|
||||
selectLastSuggestion: function () {
|
||||
var lastChild = $('li.suggestion:last-child');
|
||||
lastChild.addClass('active-suggestion');
|
||||
this._ensureVisible(lastChild, $('#autocomplete'));
|
||||
},
|
||||
|
||||
selectNextSuggestion: function () {
|
||||
var selectedSuggestion = this.getSelectedSuggestion();
|
||||
selectedSuggestion.removeClass('active-suggestion');
|
||||
var nextSuggestion = selectedSuggestion.next();
|
||||
if (nextSuggestion.length > 0) {
|
||||
nextSuggestion.addClass('active-suggestion');
|
||||
this._ensureVisible(nextSuggestion, $('#autocomplete'));
|
||||
} else {
|
||||
/* The currently selected suggestion is the last one.
|
||||
* Go back to first one. */
|
||||
this.selectFirstSuggestion();
|
||||
}
|
||||
},
|
||||
|
||||
selectPreviousSuggestion: function () {
|
||||
var selectedSuggestion = this.getSelectedSuggestion();
|
||||
selectedSuggestion.removeClass('active-suggestion');
|
||||
var previousSuggestion = selectedSuggestion.prev();
|
||||
if (previousSuggestion.length > 0) {
|
||||
previousSuggestion.addClass('active-suggestion');
|
||||
this._ensureVisible(previousSuggestion, $('#autocomplete'));
|
||||
} else {
|
||||
/* The currently selected suggestion is the first one.
|
||||
* Go back to last one. */
|
||||
this.selectLastSuggestion();
|
||||
}
|
||||
},
|
||||
|
||||
addListenerToOnDocumentChange: function () {
|
||||
var session = this._getEditSession();
|
||||
session.addEventListener('change', this.$onDocumentChange);
|
||||
},
|
||||
|
||||
removeListenerToOnDocumentChange: function () {
|
||||
var session = this._getEditSession();
|
||||
session.removeEventListener('change', this.$onDocumentChange);
|
||||
},
|
||||
|
||||
onDocumentChange: function (e) {
|
||||
|
||||
if ( e.data === undefined || e.data === null || e.data.text.search(/^\s+$/) !== -1) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
var position = null;
|
||||
if (e.data.action === 'insertText') {
|
||||
|
||||
if( codiad.autosave.saving === false ) {
|
||||
position = e.data.range.end;
|
||||
} else {
|
||||
var start = new Date().getTime();
|
||||
for (var i = 0; i < 1e7; i++) {
|
||||
if ((new Date().getTime() - start) > 50){
|
||||
break;
|
||||
}
|
||||
}
|
||||
position = e.data.range.end;
|
||||
}
|
||||
} else if (e.data.action === 'removeText') {
|
||||
position = e.data.range.start;
|
||||
} else {
|
||||
alert('Unkown document change action.');
|
||||
}
|
||||
|
||||
var foundSuggestions = this.updateSuggestions(position);
|
||||
if (!foundSuggestions) {
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
|
||||
addKeyboardCommands: function () {
|
||||
var _this = this;
|
||||
var commandManager = this._getEditor().commands;
|
||||
|
||||
/* Save the standard commands that will be overwritten. */
|
||||
this.standardGoLineDownExec = commandManager.commands.golinedown.exec;
|
||||
this.standardGoLineUpExec = commandManager.commands.golineup.exec;
|
||||
|
||||
this.standardGoToRightExec = commandManager.commands.gotoright.exec;
|
||||
this.standardGoToLeftExec = commandManager.commands.gotoleft.exec;
|
||||
this.standardIndentExec = commandManager.commands.indent.exec;
|
||||
|
||||
/* Overwrite with the completion specific implementations. */
|
||||
commandManager.commands.golinedown.exec = this.$selectNextSuggestion;
|
||||
commandManager.commands.golineup.exec = this.$selectPreviousSuggestion;
|
||||
|
||||
commandManager.commands.gotoright.exec = this.$complete;
|
||||
commandManager.commands.gotoleft.exec = this.$hide;
|
||||
commandManager.commands.indent.exec = this.$complete;
|
||||
|
||||
commandManager.addCommand({
|
||||
name: 'hideautocomplete',
|
||||
bindKey: 'Esc',
|
||||
exec: function () {
|
||||
_this.hide();
|
||||
}
|
||||
});
|
||||
|
||||
commandManager.addCommand({
|
||||
name: 'autocomplete',
|
||||
bindKey: 'Return',
|
||||
exec: this.$complete
|
||||
});
|
||||
},
|
||||
|
||||
removeKeyboardCommands: function () {
|
||||
var commandManager = this._getEditor().commands;
|
||||
|
||||
/* Make sure the standard exec have been initialized. */
|
||||
if (this.standardGoLineDownExec !== null) {
|
||||
commandManager.commands.golinedown.exec = this.standardGoLineDownExec;
|
||||
}
|
||||
|
||||
if (this.standardGoLineUpExec !== null) {
|
||||
commandManager.commands.golineup.exec = this.standardGoLineUpExec;
|
||||
}
|
||||
|
||||
if (this.standardGoToRightExec !== null) {
|
||||
commandManager.commands.gotoright.exec = this.standardGoToRightExec;
|
||||
}
|
||||
|
||||
if (this.standardGoToLeftExec !== null) {
|
||||
commandManager.commands.gotoleft.exec = this.standardGoToLeftExec;
|
||||
}
|
||||
|
||||
if (this.standardIndentExec !== null) {
|
||||
commandManager.commands.indent.exec = this.standardIndentExec;
|
||||
}
|
||||
|
||||
commandManager.removeCommand('hideautocomplete');
|
||||
commandManager.removeCommand('autocomplete');
|
||||
},
|
||||
|
||||
/* Complete the word at the given position with the currently selected
|
||||
* suggestion. Only the part of the word before the position is
|
||||
* replaced. */
|
||||
complete: function (position) {
|
||||
var editor = this._getEditor();
|
||||
var session = this._getEditSession();
|
||||
|
||||
var position = editor.getCursorPosition();
|
||||
|
||||
/* Get the length of the word being typed. */
|
||||
var token = session.getTokenAt(position.row, position.column);
|
||||
if (!token) {
|
||||
/* No token at the given position. */
|
||||
this.clearSuggestionCache();
|
||||
this.hide();
|
||||
editor.focus();
|
||||
}
|
||||
|
||||
var prefix = token.value.substr(0, position.column - token.start);
|
||||
var prefixLength = prefix.split(this.wordRegex).slice(-1)[0].length;
|
||||
|
||||
var range = new Range(position.row,
|
||||
position.column - prefixLength,
|
||||
position.row,
|
||||
position.column);
|
||||
|
||||
var suggestion = this.getSelectedSuggestion().text();
|
||||
session.replace(range, suggestion);
|
||||
|
||||
this.hide();
|
||||
editor.focus();
|
||||
},
|
||||
|
||||
/* Remove the suggestions from the Dom. */
|
||||
removeSuggestions: function () {
|
||||
$('.suggestion').remove();
|
||||
},
|
||||
|
||||
/* Get suggestions of completion for the current position in the
|
||||
* document. */
|
||||
getSuggestions: function (position) {
|
||||
|
||||
/* If suggestions are cached,
|
||||
* return them directely */
|
||||
if (this._suggestionCache) {
|
||||
return this._suggestionCache;
|
||||
}
|
||||
|
||||
var doc = this._getDocument();
|
||||
|
||||
/* FIXME For now, make suggestions on the whole file content except
|
||||
* the current token. Might be a little bit smarter, e.g., remove
|
||||
* all the keywords associated with the current language. */
|
||||
|
||||
/* Get all the text, put a marker at the cursor position. The
|
||||
* marker uses word character so that it won't be discarded by a
|
||||
* word split. */
|
||||
var markerString = '__autocomplete_marker__';
|
||||
var text = doc.getLines(0, position.row - 1).join("\n") + "\n";
|
||||
var currentLine = doc.getLine(position.row);
|
||||
text += currentLine.substr(0, position.column);
|
||||
text += markerString;
|
||||
if (position.column === currentLine.length) {
|
||||
// position is at end of line, add a break line.
|
||||
text += "\n";
|
||||
}
|
||||
text += currentLine.substr(position.column + 1);
|
||||
text += doc.getLines(position.row + 1, doc.getLength()).join("\n") + "\n";
|
||||
|
||||
/* Split the text into words. */
|
||||
var suggestions = text.split(this.wordRegex);
|
||||
|
||||
/* Get the index of the word at the cursor position. */
|
||||
var markerIndex = 0;
|
||||
var markedWord = '';
|
||||
$.each(suggestions, function (index, value) {
|
||||
if (value.search(markerString) !== -1) {
|
||||
markerIndex = index;
|
||||
markedWord = value;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
/* Build an object associating the suggestions with their distance
|
||||
* to the word at cursor position. To make sure that no suggestion
|
||||
* string overrides a built-in method of the suggestionsAndDistance
|
||||
* object, suffix all the suggestions with '-'. Afterward, make
|
||||
* sure of removing that suffix before using the stored
|
||||
* suggestions! */
|
||||
var suggestionsAndDistance = {};
|
||||
$.each(suggestions, function (index, suggestion) {
|
||||
var distance = Math.abs(index - markerIndex);
|
||||
if (!suggestionsAndDistance[suggestion + '-'] ||
|
||||
distance < suggestionsAndDistance[suggestion]) {
|
||||
suggestionsAndDistance[suggestion + '-'] = distance;
|
||||
}
|
||||
});
|
||||
|
||||
/* Remove from the suggestions the word under the cursor. */
|
||||
delete suggestionsAndDistance[markedWord + '-'];
|
||||
|
||||
/* Fill the cache */
|
||||
this._suggestionCache = suggestionsAndDistance;
|
||||
|
||||
return suggestionsAndDistance;
|
||||
},
|
||||
|
||||
/* Clear the suggestion cache */
|
||||
clearSuggestionCache: function () {
|
||||
this._suggestionCache = null;
|
||||
},
|
||||
|
||||
/* Given an object associating suggestions and their distances to the
|
||||
* word under the cursor (the prefix), return a ranked array of
|
||||
* suggestions with the best match first. The suggestions are ranked
|
||||
* based on if they match the prefix fuzzily, how much they match the
|
||||
* given prefix in the computeSimpleMatchScore sense and on their
|
||||
* distances to the prefix. */
|
||||
rankSuggestions: function (prefix, suggestionsAndDistance) {
|
||||
/* Initialize maxScore to one to ensure removing the non matching
|
||||
* suggestions (those with a zero score). */
|
||||
var maxScore = 1;
|
||||
var suggestionsAndMatchScore = {};
|
||||
for (var suggestion in suggestionsAndDistance) {
|
||||
if (Object.prototype.hasOwnProperty.call(suggestionsAndDistance, suggestion)) {
|
||||
var score = this.computeSimpleMatchScore(prefix, suggestion);
|
||||
if (score > maxScore) {
|
||||
maxScore = score;
|
||||
}
|
||||
suggestionsAndMatchScore[suggestion] = score;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove the suggestions that do not match the prefix fuzzily. */
|
||||
for (suggestion in suggestionsAndMatchScore) {
|
||||
if (Object.prototype.hasOwnProperty.call(suggestionsAndMatchScore, suggestion)) {
|
||||
if (!this.isMatchingFuzzily(prefix, suggestion)) {
|
||||
delete suggestionsAndMatchScore[suggestion];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now for each suggestion we have its matching score and its
|
||||
* distance to the word under the cursor. So compute its final
|
||||
* score as a combination of both. */
|
||||
var suggestionsAndFinalScore = {};
|
||||
for (suggestion in suggestionsAndMatchScore) {
|
||||
if (Object.prototype.hasOwnProperty.call(suggestionsAndMatchScore, suggestion)) {
|
||||
//suggestionsAndFinalScore[suggestion] = suggestionsAndMatchScore[suggestion] -
|
||||
// suggestionsAndDistance[suggestion];
|
||||
suggestionsAndFinalScore[suggestion] = suggestionsAndMatchScore[suggestion] / suggestion.length;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make an array of suggestions and make sure to rank them in the
|
||||
* ascending scores order. */
|
||||
var suggestions = [];
|
||||
for (suggestion in suggestionsAndFinalScore) {
|
||||
if (Object.prototype.hasOwnProperty.call(suggestionsAndFinalScore, suggestion)) {
|
||||
suggestions.push(suggestion);
|
||||
}
|
||||
}
|
||||
|
||||
suggestions.sort(function (firstSuggestion, secondSuggestion) {
|
||||
return suggestionsAndFinalScore[secondSuggestion] - suggestionsAndFinalScore[firstSuggestion];
|
||||
});
|
||||
|
||||
return suggestions;
|
||||
},
|
||||
|
||||
/* Return the number of consecutive letters starting from the first
|
||||
* letter in suggestion that match prefix. For instance,
|
||||
* this.computeSimpleMatchScore(cod, codiad) will return 3. If
|
||||
* suggestion is shorter than prefix, return a score of zero. The score
|
||||
* is computed using a Vim-like smartcase behavior. */
|
||||
computeSimpleMatchScore: function (prefix, suggestion) {
|
||||
/* Use a Vim-like smartcase behavior. If prefix is all lowercase,
|
||||
* compute the match score case insensitive, if it is not, compute
|
||||
* the score case sensitive. */
|
||||
var localSuggestion = this._isLowerCase(prefix) ? suggestion.toLowerCase() : suggestion;
|
||||
|
||||
if (localSuggestion.length < prefix.length) {
|
||||
return 0;
|
||||
} else if (localSuggestion === prefix) {
|
||||
return prefix.length;
|
||||
} else {
|
||||
var score = 0;
|
||||
for (var i = 0; i < prefix.length; ++i) {
|
||||
if (localSuggestion[i] === prefix[i]) {
|
||||
++score;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
},
|
||||
|
||||
/* Return true if suggestion fuzzily matches prefix. Because everybody
|
||||
* loves fuzzy matches.
|
||||
* For instance, this.isMatchingFuzzily(mlf, mylongfunctionname)
|
||||
* will return true. The score is computed using a Vim-like smartcase
|
||||
* behavior. */
|
||||
isMatchingFuzzily: function (prefix, suggestion) {
|
||||
/* Use a Vim-like smartcase behavior. If prefix is all lowercase,
|
||||
* compute the match score case insensitive, if it is not, compute
|
||||
* the score case sensitive. */
|
||||
var localSuggestion = this._isLowerCase(prefix) ? suggestion.toLowerCase() : suggestion;
|
||||
|
||||
/* Escape the characters that have a special meaning for regex in
|
||||
* the prefix. */
|
||||
var localPrefix = prefix.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
||||
|
||||
var fuzzyRegex = '^.*?';
|
||||
for (var i = 0; i < localPrefix.length; ++i) {
|
||||
if (localPrefix[i] === '\\') {
|
||||
fuzzyRegex += localPrefix[i];
|
||||
++i;
|
||||
}
|
||||
|
||||
fuzzyRegex += localPrefix[i];
|
||||
fuzzyRegex += '.*?';
|
||||
}
|
||||
|
||||
if (localSuggestion.search(fuzzyRegex) !== -1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
getMatchIndexes: function (prefix, suggestion) {
|
||||
/* Use a Vim-like smartcase behavior. If prefix is all lowercase,
|
||||
* find the match indexes case insensitive, if it is not, find them
|
||||
* case sensitive. */
|
||||
var localSuggestion = this._isLowerCase(prefix) ? suggestion.toLowerCase() : suggestion;
|
||||
|
||||
var matchIndexes = [];
|
||||
var startIndex = 0;
|
||||
for (var i = 0; i < prefix.length; ++i) {
|
||||
var index = localSuggestion.indexOf(prefix[i], startIndex);
|
||||
matchIndexes.push(index);
|
||||
startIndex = index + 1;
|
||||
}
|
||||
|
||||
return matchIndexes;
|
||||
},
|
||||
|
||||
_isLowerCase: function (str) {
|
||||
return (str.toLowerCase() === str);
|
||||
},
|
||||
|
||||
_ensureVisible: function (el, parent) {
|
||||
var offset = 1;
|
||||
var paneMin = parent.scrollTop();
|
||||
var paneMax = paneMin + parent.innerHeight();
|
||||
var itemMin = el.position().top + paneMin - offset;
|
||||
var itemMax = itemMin + el.outerHeight() + 2*offset;
|
||||
if (itemMax > paneMax) {
|
||||
parent.stop().animate({
|
||||
scrollTop: itemMax - parent.innerHeight()
|
||||
}, 100);
|
||||
} else if (itemMin < paneMin) {
|
||||
parent.stop().animate({
|
||||
scrollTop: itemMin
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
|
||||
_computeTopOffset: function () {
|
||||
/* FIXME How to handle multiple cursors? This seems to compute the
|
||||
* offset using the position of the last created cursor. */
|
||||
var cursor = $('.ace_cursor');
|
||||
if (cursor.length > 0) {
|
||||
var fontSize = codiad.editor.getActive().container.style.fontSize.replace('px', '');
|
||||
var interLine = 1.7;
|
||||
cursor = $(cursor[0]);
|
||||
var top = cursor.offset().top + fontSize * interLine;
|
||||
return top;
|
||||
}
|
||||
},
|
||||
|
||||
_computeLeftOffset: function () {
|
||||
/* FIXME How to handle multiple cursors? This seems to compute the
|
||||
* offset using the position of the last created cursor. */
|
||||
var cursor = $('.ace_cursor');
|
||||
if (cursor.length > 0) {
|
||||
cursor = $(cursor[0]);
|
||||
var left = cursor.offset().left;
|
||||
return left;
|
||||
}
|
||||
},
|
||||
|
||||
/* Set of helper methods to manipulate the editor. */
|
||||
_getEditor: function () {
|
||||
return codiad.editor.getActive();
|
||||
},
|
||||
|
||||
_getEditSession: function () {
|
||||
return codiad.editor.getActive().getSession();
|
||||
},
|
||||
|
||||
_getDocument: function () {
|
||||
return codiad.editor.getActive().getSession().getDocument();
|
||||
},
|
||||
|
||||
/* Some unit tests. */
|
||||
//i don't need to translate this... this is just for testing things...
|
||||
_testSimpleMatchScorer: function () {
|
||||
var prefix = 'myprefix';
|
||||
var suggestion = 'myprefixisshort';
|
||||
var score = this.computeSimpleMatchScore(prefix, suggestion);
|
||||
if (score !== 8) {
|
||||
alert('_testSimpleMatchScorer lowercase test failed.');
|
||||
}
|
||||
|
||||
prefix = 'MYPREFIX';
|
||||
suggestion = 'MYPREFIXISSHORT';
|
||||
score = this.computeSimpleMatchScore(prefix, suggestion);
|
||||
if (score !== 8) {
|
||||
alert('_testSimpleMatchScorer uppercase test failed.');
|
||||
}
|
||||
|
||||
prefix = 'myPrefix';
|
||||
suggestion = 'myprefixisshort';
|
||||
score = this.computeSimpleMatchScore(prefix, suggestion);
|
||||
if (score !== 2) {
|
||||
alert('_testSimpleMatchScorer mixed case vs. lowercase test failed.');
|
||||
}
|
||||
|
||||
prefix = 'myPrefixIs';
|
||||
suggestion = 'myPrefixIsShort';
|
||||
score = this.computeSimpleMatchScore(prefix, suggestion);
|
||||
if (score !== 10) {
|
||||
alert('_testSimpleMatchScorer mixed case test failed.');
|
||||
}
|
||||
},
|
||||
|
||||
_testFuzzyMatcher: function () {
|
||||
var isMatching = this.isMatchingFuzzily('mlf', 'mylongfunctionname');
|
||||
if (!isMatching) {
|
||||
alert('_testFuzzyMatcher mlf vs. mylongfunctionname failed.');
|
||||
}
|
||||
|
||||
isMatching = this.isMatchingFuzzily('mLn', 'myLongFunctionName');
|
||||
if (!isMatching) {
|
||||
alert('_testFuzzyMatcher mLn vs. myLongFunctionName failed.');
|
||||
}
|
||||
isMatching = this.isMatchingFuzzily('mLFuny', 'myLongFunctionName');
|
||||
if (isMatching) {
|
||||
alert('_testFuzzyMatcher mLFuny. myLongFunctionName failed.');
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
})(this, jQuery);
|
|
@ -74,7 +74,10 @@
|
|||
}
|
||||
});
|
||||
|
||||
console.log( 'Auto save Enabled' );
|
||||
if( codiad.auto_save.verbose ) {
|
||||
|
||||
console.log( 'Auto save Enabled' );
|
||||
}
|
||||
this.auto_save_trigger = setInterval( this.auto_save, 256 );
|
||||
},
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
var Editor = ace.require('ace/editor').Editor;
|
||||
var EditSession = ace.require('ace/edit_session').EditSession;
|
||||
var UndoManager = ace.require("ace/undomanager").UndoManager;
|
||||
|
||||
|
||||
// Editor modes that have been loaded
|
||||
var editorModes = {};
|
||||
|
||||
|
@ -386,6 +386,7 @@
|
|||
|
||||
// Settings for Editor instances
|
||||
settings: {
|
||||
autocomplete: false,
|
||||
theme: 'twilight',
|
||||
fontSize: '13px',
|
||||
printMargin: false,
|
||||
|
@ -450,8 +451,19 @@
|
|||
'tabSize',
|
||||
'theme',
|
||||
];
|
||||
var bool_options = [
|
||||
'autocomplete',
|
||||
'printMargin',
|
||||
'highlightLine',
|
||||
'indentGuides',
|
||||
'wrapMode',
|
||||
'rightSidebarTrigger',
|
||||
'fileManagerTrigger',
|
||||
'softTabs',
|
||||
'persistentModal',
|
||||
];
|
||||
|
||||
$.each(options, async function( idx, key ) {
|
||||
$.each( options, async function( idx, key ) {
|
||||
|
||||
var localValue = await codiad.settings.get_option( 'codiad.editor.' + key );
|
||||
if ( localValue !== null ) {
|
||||
|
@ -460,8 +472,7 @@
|
|||
}
|
||||
});
|
||||
|
||||
$.each(['printMargin', 'highlightLine', 'indentGuides', 'wrapMode', 'rightSidebarTrigger', 'fileManagerTrigger', 'softTabs', 'persistentModal'],
|
||||
async function(idx, key) {
|
||||
$.each( bool_options, async function(idx, key) {
|
||||
var localValue = await codiad.settings.get_option( 'codiad.editor.' + key );
|
||||
if (localValue === null) {
|
||||
return;
|
||||
|
@ -495,6 +506,11 @@
|
|||
this.setTabSize(this.settings.tabSize, i);
|
||||
this.setSoftTabs(this.settings.softTabs, i);
|
||||
this.setOverScroll(this.settings.overScroll, i);
|
||||
i.setOptions({
|
||||
enableBasicAutocompletion: true,
|
||||
enableSnippets: true,
|
||||
enableLiveAutocompletion: this.settings.autocomplete
|
||||
});
|
||||
},
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
@ -540,12 +556,13 @@
|
|||
pContainer.setChild(idx, sc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ace.require("ace/ext/language_tools");
|
||||
var i = ace.edit(el[0]);
|
||||
var resizeEditor = function(){
|
||||
i.resize();
|
||||
};
|
||||
|
||||
|
||||
if (sc) {
|
||||
i.splitContainer = sc;
|
||||
i.splitIdx = chIdx;
|
||||
|
@ -1564,6 +1581,24 @@
|
|||
//Database
|
||||
codiad.settings.update_option( 'codiad.editor.overScroll', s );
|
||||
},
|
||||
|
||||
setLiveAutocomplete: function( s, i ) {
|
||||
|
||||
if (i) {
|
||||
i.setOptions({
|
||||
enableLiveAutocompletion: s
|
||||
});
|
||||
} else {
|
||||
this.settings.autocomplete = s;
|
||||
this.forEach(function(i) {
|
||||
i.setOptions({
|
||||
enableLiveAutocompletion: s
|
||||
});
|
||||
});
|
||||
}
|
||||
//Database
|
||||
codiad.settings.update_option( 'codiad.editor.autocomplete', s );
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -140,6 +140,10 @@
|
|||
case "codiad.editor.overScroll":
|
||||
codiad.editor.setOverScroll(val);
|
||||
break;
|
||||
case "codiad.editor.autocomplete":
|
||||
var bool_val = (val == "true");
|
||||
codiad.editor.setLiveAutocomplete(bool_val)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -185,4 +185,17 @@
|
|||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
<td><?php i18n("Live Autocomplete"); ?></td>
|
||||
<td>
|
||||
|
||||
<select class="setting" data-setting="codiad.editor.autocomplete">
|
||||
<option value="false" selected><?php i18n("No"); ?></option>
|
||||
<option value="true"><?php i18n("Yes"); ?></option>
|
||||
</select>
|
||||
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -417,11 +417,12 @@ if( defined( "SITE_NAME" ) && ! ( SITE_NAME === "" || SITE_NAME === null ) ) {
|
|||
|
||||
<!-- ACE -->
|
||||
<script src="components/editor/ace-editor/ace.js"></script>
|
||||
<script src="components/editor/ace-editor/ext-language_tools.js"></script>
|
||||
|
||||
<!-- Codiad System Variables -->
|
||||
<script>
|
||||
codiad.system = {};
|
||||
codiad.system.site_id = `<?php echo SITE_ID;?>`;
|
||||
codiad.system.site_id = `<?php echo $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];?>`;
|
||||
codiad.system.session_id = `<?php echo SESSION_ID;?>`;
|
||||
</script>
|
||||
|
||||
|
|
Loading…
Reference in a new issue