mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #1533 from sharelatex/as-fix-extra-bracket
Fix autocomplete inserting extra brace when completing command with optional argument GitOrigin-RevId: cb01eb0b7df400997e5f7dc25a92ef7660689709
This commit is contained in:
parent
c6b8ab3245
commit
f84e94dc88
1 changed files with 72 additions and 122 deletions
|
@ -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
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue