diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee index 6baf2e633d..9fe956f2bb 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee @@ -5,6 +5,7 @@ define [ "ace/ext-language_tools" ], (SuggestionManager, SnippetManager) -> Range = ace.require("ace/range").Range + aceSnippetManager = ace.require('ace/snippets').snippetManager getLastCommandFragment = (lineUpToCursor) -> if m = lineUpToCursor.match(/(\\[^\\]+)$/) @@ -163,6 +164,56 @@ define [ if this.completions.filterText.match(/^\\begin\{/) and nextChar == "}" editor.session.remove(range) + # Provide our own `insertMatch` implementation. + # See the `insertMatch` method of Autocomplete in `ext-language_tools.js`. + # We need this to account for editing existing commands, particularly when + # adding a prefix. + # We fix this by detecting when the cursor is in the middle of an existing + # command, and adjusting the insertions/deletions accordingly. + # Example: + # when changing `\ref{}` to `\href{}`, ace default behaviour + # is likely to end up with `\href{}ref{}` + if !data? + completions = this.completions + popup = editor.completer.popup + data = popup.getData(popup.getRow()) + data.completer = + insertMatch: (editor, matchData) -> + for range in editor.selection.getAllRanges() + leftRange = _.clone(range) + rightRange = _.clone(range) + # trim to left of cursor + leftRange.start.column -= completions.filterText.length; + editor.session.remove(leftRange); + lineBeyondCursor = editor.getSession().getTextRange( + new Range( + rightRange.start.row, + rightRange.start.column, + rightRange.end.row, + 99999 + ) + ) + if lineBeyondCursor + if partialCommandMatch = lineBeyondCursor.match(/^([a-z0-9]+)\{/) + # We've got a partial command after the cursor + commandTail = partialCommandMatch[1] + # remove rest of the partial command, right of cursor + rightRange.end.column += commandTail.length - completions.filterText.length + editor.session.remove(rightRange); + # trim the completion text to just the command, without braces or brackets + # example: '\cite{}' -> '\cite' + if matchData.snippet? + matchData.snippet = matchData.snippet.replace(/[{\[].*[}\]]/, '') + if matchData.caption? + matchData.caption = matchData.caption.replace(/[{\[].*[}\]]/, '') + if matchData.value? + matchData.value = matchData.value.replace(/[{\[].*[}\]]/, '') + # finally, insert the match + if matchData.snippet + aceSnippetManager.insertSnippet(editor, matchData.snippet); + else + editor.execCommand("insertstring", matchData.value || matchData); + Autocomplete::_insertMatch.call this, data # Overwrite this to set autoInsert = false and set font size