From f84e94dc88f934afeb7b135bf6c479b7199b7c06 Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Mon, 25 Feb 2019 10:24:10 +0000 Subject: [PATCH] Merge pull request #1533 from sharelatex/as-fix-extra-bracket Fix autocomplete inserting extra brace when completing command with optional argument GitOrigin-RevId: cb01eb0b7df400997e5f7dc25a92ef7660689709 --- .../auto-complete/AutoCompleteManager.js | 194 +++++++----------- 1 file changed, 72 insertions(+), 122 deletions(-) diff --git a/services/web/public/src/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.js b/services/web/public/src/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.js index 54764ebc20..24f2b6e872 100644 --- a/services/web/public/src/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.js +++ b/services/web/public/src/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.js @@ -1,22 +1,4 @@ -/* eslint-disable - max-len, - no-cond-assign, - no-return-assign, - no-undef, - no-unused-vars, - no-useless-escape, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS101: Remove unnecessary use of Array.from - * DS102: Remove unnecessary code created because of implicit returns - * DS103: Rewrite code to no longer use __guard__ - * DS202: Simplify dynamic range loops - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ +/* global _ */ define([ 'ide/editor/directives/aceEditor/auto-complete/CommandManager', 'ide/editor/directives/aceEditor/auto-complete/EnvironmentManager', @@ -25,11 +7,10 @@ define([ 'ace/ace', 'ace/ext-language_tools' ], function(CommandManager, EnvironmentManager, PackageManager, Helpers) { - let AutoCompleteManager const { Range } = ace.require('ace/range') const aceSnippetManager = ace.require('ace/snippets').snippetManager - return (AutoCompleteManager = class AutoCompleteManager { + class AutoCompleteManager { constructor( $scope, editor, @@ -50,19 +31,19 @@ define([ this.$scope.$watch('autoComplete', autocomplete => { if (autocomplete) { - return this.enable() + this.enable() } else { - return this.disable() + this.disable() } }) const onChange = change => { - return this.onChange(change) + this.onChange(change) } this.editor.on('changeSession', e => { e.oldSession.off('change', onChange) - return e.session.on('change', onChange) + e.session.on('change', onChange) }) } @@ -89,14 +70,13 @@ define([ /^~?\\(includegraphics(?:\[.*])?){([^}]*, *)?(\w*)/ ) if (match) { - let [_ignore1, commandName, _ignore2, currentArg] = Array.from( - match - ) + // eslint-disable-next-line no-unused-vars + let commandName = match[1] const graphicsPaths = Preamble.getGraphicsPaths() const result = [] - for (let graphic of Array.from(Graphics.getGraphicsFiles())) { + for (let graphic of Graphics.getGraphicsFiles()) { let { path } = graphic - for (let graphicsPath of Array.from(graphicsPaths)) { + for (let graphicsPath of graphicsPaths) { if (path.indexOf(graphicsPath) === 0) { path = path.slice(graphicsPath.length) break @@ -109,7 +89,7 @@ define([ score: 50 }) } - return callback(null, result) + callback(null, result) } } } @@ -122,9 +102,10 @@ define([ if (commandFragment) { const match = commandFragment.match(/^\\(input|include){(\w*)/) if (match) { - const [_, commandName, currentArg] = Array.from(match) + // eslint-disable-next-line no-unused-vars + const commandName = match[1] const result = [] - for (let file of Array.from(Files.getTeXFiles())) { + for (let file of Files.getTeXFiles()) { if (file.id !== this.$scope.docId) { const { path } = file result.push({ @@ -135,7 +116,7 @@ define([ }) } } - return callback(null, result) + callback(null, result) } } } @@ -149,7 +130,8 @@ define([ /^~?\\([a-zA-Z]*ref){([^}]*, *)?(\w*)/ ) if (refMatch) { - const [_, commandName, currentArg] = Array.from(refMatch) + // eslint-disable-next-line no-unused-vars + const commandName = refMatch[1] const result = [] if (commandName !== 'ref') { // ref is in top 100 commands @@ -160,7 +142,7 @@ define([ score: 60 }) } - for (let label of Array.from(metadataManager.getAllLabels())) { + for (let label of metadataManager.getAllLabels()) { result.push({ caption: `\\${commandName}{${label}}`, value: `\\${commandName}{${label}}`, @@ -168,7 +150,7 @@ define([ score: 50 }) } - return callback(null, result) + callback(null, result) } } } @@ -183,9 +165,8 @@ define([ /^~?\\([a-z]*cite[a-z]*(?:\[.*])?){([^}]*, *)?(\w*)/ ) if (citeMatch) { - let [_, commandName, previousArgs, currentArg] = Array.from( - citeMatch - ) + // eslint-disable-next-line no-unused-vars + let [_ignore, commandName, previousArgs] = citeMatch if (previousArgs == null) { previousArgs = '' } @@ -209,16 +190,16 @@ define([ }) } }) - return callback(null, result) + callback(null, result) } else { - return callback(null, result) + callback(null, result) } } } } } - return (this.editor.completers = [ + this.editor.completers = [ CommandCompleter, SnippetCompleter, PackageCompleter, @@ -226,7 +207,7 @@ define([ LabelsCompleter, GraphicsCompleter, FilesCompleter - ]) + ] } disable() { @@ -254,16 +235,15 @@ define([ const lastTwoChars = lineUpToCursor.slice(-2) // Don't offer autocomplete on double-backslash, backslash-colon, etc if (/^\\[^a-zA-Z]$/.test(lastTwoChars)) { - __guardMethod__( - this.editor != null ? this.editor.completer : undefined, - 'detach', - o => o.detach() - ) + if (this.editor.completer) { + this.editor.completer.detach() + } return } // Check that this change was made by us, not a collaborator // (Cursor is still one place behind) - // NOTE: this is also the case when a user backspaces over a highlighted region + // NOTE: this is also the case when a user backspaces over a highlighted + // region if ( change.action === 'insert' && end.row === cursorPosition.row && @@ -274,18 +254,21 @@ define([ lastCharIsBackslash ) { setTimeout(() => { - return this.editor.execCommand('startAutocomplete') + this.editor.execCommand('startAutocomplete') }, 0) } } + const match = change.lines[0].match(/\\(\w+){}/) if ( change.action === 'insert' && + (match && match[1]) && + // eslint-disable-next-line max-len /(begin|end|[a-zA-Z]*ref|usepackage|[a-z]*cite[a-z]*|input|include)/.test( - __guard__(change.lines[0].match(/\\(\w+){}/), x => x[1]) + match[1] ) ) { return setTimeout(() => { - return this.editor.execCommand('startAutocomplete') + this.editor.execCommand('startAutocomplete') }, 0) } } @@ -306,16 +289,21 @@ define([ // If we are in \begin{it|}, then we need to remove the trailing } // since it will be adding in with the autocomplete of \begin{item}... - if (/^\\\w+{/.test(this.completions.filterText) && nextChar === '}') { + if ( + /^\\\w+(\[[\w\\,= ]*\])?{/.test(this.completions.filterText) && + 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. + // 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{}` @@ -325,7 +313,7 @@ define([ data = popup.getData(popup.getRow()) data.completer = { insertMatch(editor, matchData) { - for (range of Array.from(editor.selection.getAllRanges())) { + for (range of editor.selection.getAllRanges()) { const leftRange = _.clone(range) const rightRange = _.clone(range) // trim to left of cursor @@ -362,35 +350,34 @@ define([ ) if (lineBeyondCursor) { - var partialCommandMatch - if ( - (partialCommandMatch = lineBeyondCursor.match( - /^([a-zA-Z0-9]+)\{/ - )) - ) { + const partialCommandMatch = lineBeyondCursor.match( + /^([a-zA-Z0-9]+)\{/ + ) + if (partialCommandMatch) { // We've got a partial command after the cursor const 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 + // trim the completion text to just the command, without + // braces or brackets // example: '\cite{}' -> '\cite' if (matchData.snippet != null) { matchData.snippet = matchData.snippet.replace( - /[{\[].*[}\]]/, + /[{[].*[}\]]/, '' ) } if (matchData.caption != null) { matchData.caption = matchData.caption.replace( - /[{\[].*[}\]]/, + /[{[].*[}\]]/, '' ) } if (matchData.value != null) { matchData.value = matchData.value.replace( - /[{\[].*[}\]]/, + /[{[].*[}\]]/, '' ) } @@ -399,12 +386,9 @@ define([ } // finally, insert the match if (matchData.snippet) { - return aceSnippetManager.insertSnippet( - editor, - matchData.snippet - ) + aceSnippetManager.insertSnippet(editor, matchData.snippet) } else { - return editor.execCommand( + editor.execCommand( 'insertstring', matchData.value || matchData ) @@ -413,7 +397,7 @@ define([ } } - return Autocomplete.prototype._insertMatch.call(this, data) + Autocomplete.prototype._insertMatch.call(this, data) } // Overwrite this to set autoInsert = false and set font size @@ -435,15 +419,10 @@ define([ ) container.css({ 'font-size': this.$scope.fontSize + 'px' }) // Dynamically set width of autocomplete popup - if ( - (filtered = __guard__( - __guard__( - editor != null ? editor.completer : undefined, - x1 => x1.completions - ), - x => x.filtered - )) - ) { + filtered = + editor.completer.completions && + editor.completer.completions.filtered + if (filtered) { const longestCaption = _.max(filtered.map(c => c.caption.length)) const longestMeta = _.max(filtered.map(c => c.meta.length)) const charWidth = editor.renderer.characterWidth @@ -461,31 +440,17 @@ define([ ) container.css({ width: `${width}px` }) } - if ( - __guard__( - __guard__( - editor.completer != null - ? editor.completer.completions - : undefined, - x3 => x3.filtered - ), - x2 => x2.length - ) === 0 - ) { - return editor.completer.detach() + if (filtered.length === 0) { + editor.completer.detach() } }, bindKey: 'Ctrl-Space|Ctrl-Shift-Space|Alt-Space' } } - return (Util.retrievePrecedingIdentifier = function(text, pos, regex) { + Util.retrievePrecedingIdentifier = function(text, pos, regex) { let currentLineOffset = 0 - for ( - let start = pos - 1, i = start, asc = start <= 0; - asc ? i <= 0 : i >= 0; - asc ? i++ : i-- - ) { + for (let i = pos - 1; i <= 0; i++) { if (text[i] === '\n') { currentLineOffset = i + 1 break @@ -494,24 +459,9 @@ define([ const currentLine = text.slice(currentLineOffset, pos) const fragment = Helpers.getLastCommandFragment(currentLine) || '' return fragment - }) + } } - }) -}) - -function __guardMethod__(obj, methodName, transform) { - if ( - typeof obj !== 'undefined' && - obj !== null && - typeof obj[methodName] === 'function' - ) { - return transform(obj, methodName) - } else { - return undefined } -} -function __guard__(value, transform) { - return typeof value !== 'undefined' && value !== null - ? transform(value) - : undefined -} + + return AutoCompleteManager +})