فهرست منبع

Merge branch 'master' of github.com:sharelatex/web-sharelatex

Henry Oswald 12 سال پیش
والد
کامیت
1842a65b83

+ 36 - 127
services/web/public/coffee/auto-complete/AutoCompleteManager.coffee

@@ -1,40 +1,50 @@
 define [
-	"auto-complete/MenuView"
 	"auto-complete/SuggestionManager"
+	"auto-complete/Snippets"
+	"ace/autocomplete/util"
 	"ace/range"
-], (MenuView, SuggestionManager) ->
+	"ace/ext/language_tools"
+], (SuggestionManager, Snippets, Util) ->
 	Range = require("ace/range").Range
 
+	Util.retrievePrecedingIdentifier = (text, pos, regex) ->
+		currentLineOffset = 0
+		for i in [(pos-1)..0]
+			if text[i] == "\n"
+				currentLineOffset = i + 1
+				break
+		currentLine = text.slice(currentLineOffset, pos)
+		fragment = getLastCommandFragment(currentLine) or ""
+		return fragment
+
+	getLastCommandFragment = (lineUpToCursor) ->
+		if m = lineUpToCursor.match(/(\\[^\\ ]+)$/)
+			return m[1]
+		else
+			return null
+
 	class AutoCompleteManager
 		constructor: (@ide) ->
 			@aceEditor = @ide.editor.aceEditor
-			@menu = new MenuView()
-			@menu.render(
-				@getAceContentEl().css("font-family"),
-				@getAceContentEl().css("font-size")
-			)
-			@ide.mainAreaManager.getAreaElement("editor").append(@menu.$el)
-			@menu.on "click", (e, suggestion) => @insertSuggestion(suggestion)
-			@menuVisible = false
+			@aceEditor.setOptions({
+				enableBasicAutocompletion: true,
+				enableSnippets: true
+			})
+
+			SnippetCompleter =
+				getCompletions: (editor, session, pos, prefix, callback) ->
+					callback null, Snippets
 			@suggestionManager = new SuggestionManager()
+
+			@aceEditor.completers = [@suggestionManager, SnippetCompleter]
+
 			@bindToEditorEvents()
-			@bindToAceInputEvents()
 
 		bindToEditorEvents: () ->
 			@ide.editor.on "change:doc", (@aceSession) =>
-				@refreshSuggestionList()
 				@aceSession.on "change", (change) => @onChange(change)
-			@ide.editor.on "scroll", () =>
-				@hideMenu()
-
-		bindToAceInputEvents: () ->
-			@oldOnCommandKey = @aceEditor.keyBinding.onCommandKey
-			@aceEditor.keyBinding.onCommandKey = () => @onKeyPress.apply(@, arguments)
-			$(@aceEditor.renderer.getContainerElement()).on "click", (e) => @onClick(e)
 
 		onChange: (change) ->
-			@scheduleSuggestionListRefresh()
-
 			cursorPosition = @aceEditor.getCursorPosition()
 			end = change.data.range.end
 			# Check that this change was made by us, not a collaborator
@@ -43,110 +53,9 @@ define [
 				if change.data.action == "insertText"
 					range = new Range(end.row, 0, end.row, end.column)
 					lineUpToCursor = @aceSession.getTextRange(range)
-					commandFragment = @getLastCommandFragment(lineUpToCursor)
-
-					if commandFragment
-						suggestions = @suggestionManager.getSuggestions(commandFragment)
-						if suggestions.length > 0
-							@positionMenu(commandFragment.length)
-							@menu.setSuggestions suggestions
-							@showMenu()
-						else
-							@hideMenu()
-					else
-						@hideMenu()
-				else
-					@hideMenu()
-
-		onKeyPress: (e) ->
-			keyCode = e.keyCode
-
-			args = arguments
-			delegate = () =>
-				@oldOnCommandKey.apply(@aceEditor.keyBinding, args)
-				
-			if @menuVisible
-				switch keyCode
-					when @keyCodes.UP
-						@menu.moveSelectionUp()
-					when @keyCodes.DOWN
-						@menu.moveSelectionDown()
-					when @keyCodes.ENTER, @keyCodes.TAB
-						@insertSuggestion(@menu.getSelectedSuggestion())
-						e.preventDefault()
-						@hideMenu()
-					when @keyCodes.ESCAPE
-						@hideMenu()
-					else
-						delegate()
-			else
-				delegate()
-
-		positionMenu: (characterOffset) ->
-			characterWidth = @getAceRenderer().characterWidth
-			lineHeight = @getAceRenderer().lineHeight
-
-			pos = @getCursorOffset()
-			pos.top = pos.top + lineHeight
-			styleOffset = 10 # CSS borders and margins
-			pos.left = pos.left - styleOffset - characterOffset * characterWidth
-
-			# We need to position the menu with coordinates relative to the
-			# editor area.
-			editorAreaOffset = @ide.mainAreaManager.getAreaElement("editor").offset()
-			aceOffset = @getAceContentEl().offset()
-			@menu.position
-				top: aceOffset.top - editorAreaOffset.top + pos.top
-				left: aceOffset.left - editorAreaOffset.left + pos.left
-			
-
-		insertSuggestion: (suggestion) ->
-			if suggestion?
-				oldCursorPosition = @aceEditor.getCursorPosition()
-				@aceEditor.insert(suggestion.completion)
-				@aceEditor.moveCursorTo(
-					oldCursorPosition.row,
-					oldCursorPosition.column + suggestion.completionBeforeCursor.length
-				)
-			@hideMenu()
-			@aceEditor.focus()
-
-		scheduleSuggestionListRefresh: () ->
-			clearTimeout(@updateTimeoutId) if @updateTimeoutId?
-			@updateTimeoutId = setTimeout((() =>
-				@refreshSuggestionList()
-				delete @updateTimeoutId
-			), 5000)
-
-		refreshSuggestionList: () ->
-			@suggestionManager.loadCommandsFromDoc(@aceSession.doc.getAllLines().join("\n"))
-
-		onClick: () ->
-			@hideMenu()
-
-		getLastCommandFragment: (line) ->
-			if m = line.match(/\\([^\\ ]+)$/)
-				m[1]
-			else
-				null
-
-		showMenu: () ->
-			@menu.show()
-			@menuVisible = true
-	
-		hideMenu: () ->
-			@menu.hide()
-			@menuVisible = false
-
-		keyCodes: "UP": 38, "DOWN": 40, "ENTER": 13, "TAB": 9, "ESCAPE": 27
-
-		getCursorOffset: () ->
-			# This is fragile and relies on the internal Ace API not changing.
-			# See $moveTextAreaToCursor in 
-			# https://github.com/ajaxorg/ace/blob/master/lib/ace/virtual_renderer.js
-			@aceEditor.renderer.$cursorLayer.$pixelPos
-
-		getAceRenderer: () -> @aceEditor.renderer
-
-		getAceContentEl: () -> $(@aceEditor.renderer.getContainerElement()).find(".ace_content")
+					commandFragment = getLastCommandFragment(lineUpToCursor)
 
+					if commandFragment? and commandFragment.length > 2
+					 	setTimeout () =>
+					 		@aceEditor.execCommand("startAutocomplete")
+					 	, 0

+ 0 - 57
services/web/public/coffee/auto-complete/MenuView.coffee

@@ -1,57 +0,0 @@
-define [
-	"libs/backbone"
-	"libs/mustache"
-], () ->
-	MenuView = Backbone.View.extend
-		tagName: "ul"
-		className: "auto-complete-menu"
-
-		templates:
-			suggestion: $("#autoCompleteSuggestionTemplate").html()
-
-		render: (fontFamily, fontSize) ->
-			@$el.css
-				position: "absolute"
-				"font-family": fontFamily
-				"font-size": fontSize
-			return @$el
-
-		setSuggestions: (suggestions) ->
-			@$el.children().off()
-			@$el.empty()
-			@suggestions = []
-			for suggestion in suggestions
-				do (suggestion) =>
-					el = $(Mustache.to_html(@templates.suggestion, suggestion))
-					@$el.append(el)
-					el.on "click", (e) => @trigger("click", e, suggestion)
-					@suggestions.push suggestion: suggestion, el: el
-			@selectSuggestionAtIndex 0
-
-		selectSuggestionAtIndex: (index) ->
-			if index >= 0 and index < @suggestions.length
-				@$("li").removeClass "selected"
-				@suggestions[index].el.addClass "selected"
-				@selectedIndex = index
-
-		moveSelectionDown: () ->
-			if @selectedIndex? and @selectedIndex < @suggestions.length - 1
-				@selectSuggestionAtIndex @selectedIndex + 1
-
-		moveSelectionUp: () ->
-			if @selectedIndex? and @selectedIndex > 0
-				@selectSuggestionAtIndex @selectedIndex - 1
-
-		getSelectedSuggestion: () ->
-			if @selectedIndex? and @suggestions[@selectedIndex]?
-				@suggestions[@selectedIndex].suggestion
-
-		position: (pos) ->
-			@$el.css
-				top: pos.top
-				left: pos.left
-
-		show: () -> @$el.show()
-		hide: () -> @$el.hide()
-
-

+ 92 - 0
services/web/public/coffee/auto-complete/Snippets.coffee

@@ -0,0 +1,92 @@
+define () ->
+	environments = [
+		"abstract", 
+		"align", "align*",
+		"equation", "equation*",
+		"gather", "gather*",
+		"mutliline", "multiline*",
+		"split",
+		"verbatim"
+	]
+
+	snippets = for env in environments
+		{
+			caption: "\\begin{#{env}}..."
+			snippet: """
+				\\begin{#{env}}
+				$1
+				\\end{#{env}}
+			"""
+			meta: "env"
+		}
+
+	snippets = snippets.concat [{
+		caption: "\\begin{array}..."
+		snippet: """
+			\\begin{array}{${1:cc}}
+			$2 & $3 \\\\\\\\
+			$4 & $5
+			\\end{array}"
+		"""
+		meta: "env"
+	}, {
+		caption: "\\begin{figure}..."
+		snippet: """
+			\\begin{figure}
+			\\centering
+			\\includegraphics{$1}
+			\\caption{${2:Caption}}
+			\\label{${3:fig:my_label}}
+			\\end{figure}
+		"""
+		meta: "env"
+	}, {
+		caption: "\\begin{tabular}..."
+		snippet: """
+			\\begin{tabular}{${1:c|c}}
+			$2 & $3 \\\\\\\\
+			$4 & $5
+			\\end{tabular}
+		"""
+		meta: "env"
+	}, {
+		caption: "\\begin{table}..."
+		snippet: """
+			\\begin{table}[$1]
+			\\centering
+			\\begin{tabular}{${2:c|c}}
+			$3 & $4 \\\\\\\\
+			$5 & $6
+			\\end{tabular}
+			\\caption{${7:Caption}}
+			\\label{${8:tab:my_label}}
+			\\end{table}
+		"""
+		meta: "env"
+	}, {
+		caption: "\\begin{list}..."
+		snippet: """
+			\\begin{list}
+			\\item $1
+			\\end{list}
+		"""
+		meta: "env"
+	}, {
+		caption: "\\begin{enumerate}..."
+		snippet: """
+			\\begin{enumerate}
+			\\item $1
+			\\end{enumerate}
+		"""
+		meta: "env"
+	}, {
+		caption: "\\begin{frame}..."
+		snippet: """
+			\\begin{frame}{${1:Frame Title}}
+			$2
+			\\end{frame}
+		"""
+		meta: "env"
+	}]
+
+	return snippets

+ 27 - 5
services/web/public/coffee/auto-complete/SuggestionManager.coffee

@@ -1,6 +1,4 @@
-define [
-	"auto-complete/commands"
-], (commands) ->
+define [], () ->
 	class Parser
 		constructor: (@doc) ->
 
@@ -66,8 +64,32 @@ define [
 				return false
 
 	class SuggestionManager
-		constructor: () ->
-			@commands = []
+		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)

+ 0 - 64
services/web/public/coffee/auto-complete/commands.coffee

@@ -1,64 +0,0 @@
-define () -> [
-	# [<command>, <square brackets args>, <curly bracket args>]
-	# E.g. ["includegraphics", 1 ,1] => \includegraphics[]{}
-
-	# Common
-	["emph", 0, 1]
-
-	# Greek letters
-	["alpha", 0, 0]
-	["beta", 0, 0]
-	["gamma", 0, 0]
-	["delta", 0, 0]
-	["eta", 0, 0]
-	["theta", 0, 0]
-	["iota", 0, 0]
-	["kappa", 0, 0]
-	["lambda", 0, 0]
-	["phi", 0, 0]
-	["psi", 0, 0]
-	["mu", 0, 0]
-	["nu", 0, 0]
-	["chi", 0, 0]
-	["xsi", 0, 0]
-	["upsilon", 0, 0]
-	["Lambda", 0, 0]
-	["Omega", 0, 0]
-	["Gamma", 0, 0]
-	["Delta", 0, 0]
-
-	# Maths
-	["infty", 0, 0]
-	["frac", 0, 2]
-	["int", 0, 0]
-	["sum", 0, 0]
-	["sin", 0, 0]
-	["cos", 0, 0]
-
-	# LaTeX commands
-	["begin", 0, 1]
-	["end", 0, 1]
-	["includegraphics", 0, 1]
-	["includegraphics", 1, 1]
-	["section", 0, 1]
-	["chapter", 0, 1]
-	["subsection", 0, 1]
-	["subsubsection", 0, 1]
-	["part", 0, 1]
-	["author", 0, 1]
-	["title", 0, 1]
-	["documentclass", 0, 1]
-	["documentclass", 1, 1]
-	["usepackage", 0, 1]
-	["usepackage", 1, 1]
-
-	# Font commands
-	["textit", 0, 1]
-	["textrm", 0, 1]
-	["textsf", 0, 1]
-	["texttt", 0, 1]
-
-	["newcommand", 0, 2]
-	["renewcommand", 0, 2]
-	["newenvironment", 0, 3]
-]

+ 0 - 2
services/web/public/coffee/editor/Editor.coffee

@@ -125,12 +125,10 @@ define [
 
 			mode = window.userSettings.mode
 			theme = window.userSettings.theme
-			fontSize = window.userSettings.fontSize
 
 			chosenKeyBindings = keybindings[mode]
 			aceEditor.setKeyboardHandler(chosenKeyBindings)
 			aceEditor.setTheme("ace/theme/#{window.userSettings.theme}")
-			document.getElementById('editor').style.fontSize = fontSize+'px'
 			aceEditor.setShowPrintMargin(false)
 
 			# Prevert Ctrl|Cmd-S from triggering save dialog

+ 11 - 0
services/web/public/coffee/settings/SettingsManager.coffee

@@ -17,6 +17,8 @@ define [
 
 			new DropboxSettingsManager @ide
 
+			@setFontSize()
+
 			if @ide?
 				@ide.on "afterJoinProject", (project) =>
 					@project = project
@@ -102,6 +104,15 @@ define [
 					$confirm.off 'click'
 				$modal.find('.cancel').click (e)->
 					$modal.modal('hide')
+
+		setFontSize: () ->
+			@fontSizeCss = $("<style/>")
+			@fontSizeCss.text """
+				.ace_editor, .ace_content {
+					font-size: #{window.userSettings.fontSize}px;
+				}
+			"""
+			$(document.body).append(@fontSizeCss)
 	
 		bindToProjectName: () ->
 			@project.on "change:name", (project, newName) ->