Просмотр исходного кода

Merge pull request #1533 from sharelatex/as-fix-extra-bracket

Fix autocomplete inserting extra brace when completing command with optional argument

GitOrigin-RevId: cb01eb0b7df400997e5f7dc25a92ef7660689709
Simon Detheridge 7 лет назад
Родитель
Сommit
f84e94dc88

+ 72 - 122
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
+})