mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-29 15:23:10 +00:00
126 lines
3 KiB
CoffeeScript
126 lines
3 KiB
CoffeeScript
define [], () ->
|
|
class Parser
|
|
constructor: (@doc) ->
|
|
|
|
parse: () ->
|
|
commands = []
|
|
seen = {}
|
|
while command = @nextCommand()
|
|
docState = @doc
|
|
|
|
optionalArgs = 0
|
|
while @consumeArgument("[", "]")
|
|
optionalArgs++
|
|
|
|
args = 0
|
|
while @consumeArgument("{", "}")
|
|
args++
|
|
|
|
commandHash = "#{command}\\#{optionalArgs}\\#{args}"
|
|
if !seen[commandHash]?
|
|
seen[commandHash] = true
|
|
commands.push [command, optionalArgs, args]
|
|
|
|
# Reset to before argument to handle nested commands
|
|
@doc = docState
|
|
|
|
return commands
|
|
|
|
# Ignore single letter commands since auto complete is moot then.
|
|
commandRegex: /\\([a-zA-Z][a-zA-Z]+)/
|
|
|
|
nextCommand: () ->
|
|
i = @doc.search(@commandRegex)
|
|
if i == -1
|
|
return false
|
|
else
|
|
match = @doc.match(@commandRegex)[1]
|
|
@doc = @doc.substr(i + match.length + 1)
|
|
return match
|
|
|
|
consumeWhitespace: () ->
|
|
match = @doc.match(/^[ \t\n]*/m)[0]
|
|
@doc = @doc.substr(match.length)
|
|
|
|
consumeArgument: (openingBracket, closingBracket) ->
|
|
@consumeWhitespace()
|
|
|
|
if @doc[0] == openingBracket
|
|
i = 1
|
|
bracketParity = 1
|
|
while bracketParity > 0 and i < @doc.length
|
|
if @doc[i] == openingBracket
|
|
bracketParity++
|
|
else if @doc[i] == closingBracket
|
|
bracketParity--
|
|
i++
|
|
|
|
if bracketParity == 0
|
|
@doc = @doc.substr(i)
|
|
return true
|
|
else
|
|
return false
|
|
else
|
|
return false
|
|
|
|
class SuggestionManager
|
|
getCompletions: (editor, session, pos, prefix, callback) ->
|
|
doc = session.getValue()
|
|
parser = new Parser(doc)
|
|
commands = parser.parse()
|
|
|
|
completions = []
|
|
for command in commands
|
|
caption = "\\#{command[0]}"
|
|
snippet = caption
|
|
i = 1
|
|
_.times command[1], () ->
|
|
snippet += "[${#{i}}]"
|
|
caption += "[]"
|
|
i++
|
|
_.times command[2], () ->
|
|
snippet += "{${#{i}}}"
|
|
caption += "{}"
|
|
i++
|
|
unless caption == prefix
|
|
completions.push {
|
|
caption: caption
|
|
snippet: snippet
|
|
meta: "cmd"
|
|
}
|
|
|
|
callback null, completions
|
|
|
|
loadCommandsFromDoc: (doc) ->
|
|
parser = new Parser(doc)
|
|
@commands = parser.parse()
|
|
|
|
getSuggestions: (commandFragment) ->
|
|
matchingCommands = _.filter @commands, (command) ->
|
|
command[0].slice(0, commandFragment.length) == commandFragment
|
|
|
|
return _.map matchingCommands, (command) ->
|
|
base = "\\" + commandFragment
|
|
|
|
args = ""
|
|
_.times command[1], () -> args = args + "[]"
|
|
_.times command[2], () -> args = args + "{}"
|
|
completionBase = command[0].slice(commandFragment.length)
|
|
|
|
squareArgsNo = command[1]
|
|
curlyArgsNo = command[2]
|
|
totalArgs = squareArgsNo + curlyArgsNo
|
|
if totalArgs == 0
|
|
completionBeforeCursor = completionBase
|
|
completionAfterCurspr = ""
|
|
else
|
|
completionBeforeCursor = completionBase + args[0]
|
|
completionAfterCursor = args.slice(1)
|
|
|
|
return {
|
|
base: base,
|
|
completion: completionBase + args,
|
|
completionBeforeCursor: completionBeforeCursor
|
|
completionAfterCursor: completionAfterCursor
|
|
}
|
|
|