|
|
@@ -0,0 +1,126 @@
|
|
|
+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
|
|
|
+ }
|
|
|
+
|