diff --git a/services/web/app/coffee/Features/ProjectMetadata/MetadataController.coffee b/services/web/app/coffee/Features/ProjectMetadata/MetadataController.coffee index 81baa2bf93..a6da79f010 100644 --- a/services/web/app/coffee/Features/ProjectMetadata/MetadataController.coffee +++ b/services/web/app/coffee/Features/ProjectMetadata/MetadataController.coffee @@ -5,6 +5,30 @@ logger = require 'logger-sharelatex' module.exports = MetadataController = + getAllLabels: (req, res, next) -> + project_id = req.params.project_id + logger.log {project_id}, "getting labels for project" + MetadataHandler.getMetadataForProject project_id, (err, projectMetadata) -> + if err? + logger.err {project_id, err}, "[MetadataController] error getting labels from project" + return next err + res.json { + projectId: project_id + projectLabels: projectMetadata["labels"] + } + + getAllPackages: (req, res, next) -> + project_id = req.params.project_id + logger.log {project_id}, "getting labels for project" + MetadataHandler.getMetadataForProject project_id, (err, projectMetadata) -> + if err? + logger.err {project_id, err}, "[MetadataController] error getting labels from project" + return next err + res.json { + projectId: project_id + projectPackages: projectMetadata["packages"] + } + getAllMetadata: (req, res, next) -> project_id = req.params.project_id logger.log {project_id}, "getting metadata for project" @@ -33,4 +57,4 @@ module.exports = MetadataController = packages: docMetadata["packages"] } } - res.sendStatus(200) + res.sendStatus 200 diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee index 40ae0bb06f..6368de5044 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor.coffee @@ -9,11 +9,13 @@ define [ "ide/editor/directives/aceEditor/highlights/HighlightsManager" "ide/editor/directives/aceEditor/cursor-position/CursorPositionManager" "ide/editor/directives/aceEditor/track-changes/TrackChangesManager" - "ide/editor/directives/aceEditor/labels/LabelsManager" - "ide/labels/services/labels" + # "ide/editor/directives/aceEditor/labels/LabelsManager" + "ide/editor/directives/aceEditor/metadata/MetadataManager" + # "ide/labels/services/labels" + "ide/metadata/services/metadata" "ide/graphics/services/graphics" "ide/preamble/services/preamble" -], (App, Ace, SearchBox, ModeList, UndoManager, AutoCompleteManager, SpellCheckManager, HighlightsManager, CursorPositionManager, TrackChangesManager, LabelsManager) -> +], (App, Ace, SearchBox, ModeList, UndoManager, AutoCompleteManager, SpellCheckManager, HighlightsManager, CursorPositionManager, TrackChangesManager, MetadataManager) -> EditSession = ace.require('ace/edit_session').EditSession ModeList = ace.require('ace/ext/modelist') @@ -35,7 +37,7 @@ define [ url = ace.config._moduleUrl(args...) + "?fingerprint=#{window.aceFingerprint}" return url - App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels, graphics, preamble) -> + App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, metadata, graphics, preamble) -> monkeyPatchSearch($rootScope, $compile) return { @@ -102,8 +104,9 @@ define [ highlightsManager = new HighlightsManager(scope, editor, element) cursorPositionManager = new CursorPositionManager(scope, editor, element, localStorage) trackChangesManager = new TrackChangesManager(scope, editor, element) - labelsManager = new LabelsManager(scope, editor, element, labels) - autoCompleteManager = new AutoCompleteManager(scope, editor, element, labelsManager, graphics, preamble) + metadataManager = new metadataManager(scope, editor, element, metadata) + # labelsManager = new LabelsManager(scope, editor, element, labels) + autoCompleteManager = new AutoCompleteManager(scope, editor, element, metadataManager, graphics, preamble) # Prevert Ctrl|Cmd-S from triggering save dialog @@ -115,16 +118,16 @@ define [ editor.commands.removeCommand "transposeletters" editor.commands.removeCommand "showSettingsMenu" editor.commands.removeCommand "foldall" - + # For European keyboards, the / is above 7 so needs Shift pressing. - # This comes through as Command-Shift-/ on OS X, which is mapped to + # This comes through as Command-Shift-/ on OS X, which is mapped to # toggleBlockComment. # This doesn't do anything for LaTeX, so remap this to togglecomment to # work for European keyboards as normal. # On Windows, the key combo comes as Ctrl-Shift-7. editor.commands.removeCommand "toggleBlockComment" editor.commands.removeCommand "togglecomment" - + editor.commands.addCommand { name: "togglecomment", bindKey: { win: "Ctrl-/|Ctrl-Shift-7", mac: "Command-/|Command-Shift-/" }, @@ -140,7 +143,7 @@ define [ exec: (editor) -> ace.require("ace/ext/searchbox").Search(editor, true) readOnly: true - + # Bold text on CMD+B editor.commands.addCommand name: "bold", @@ -154,7 +157,7 @@ define [ text = editor.getCopyText() editor.insert("\\textbf{" + text + "}") readOnly: false - + # Italicise text on CMD+I editor.commands.addCommand name: "italics", @@ -171,7 +174,7 @@ define [ scope.$watch "onCtrlEnter", (callback) -> if callback? - editor.commands.addCommand + editor.commands.addCommand name: "compile", bindKey: win: "Ctrl-Enter", mac: "Command-Enter" exec: (editor) => @@ -180,7 +183,7 @@ define [ scope.$watch "onCtrlJ", (callback) -> if callback? - editor.commands.addCommand + editor.commands.addCommand name: "toggle-review-panel", bindKey: win: "Ctrl-J", mac: "Command-J" exec: (editor) => @@ -189,7 +192,7 @@ define [ scope.$watch "onCtrlShiftC", (callback) -> if callback? - editor.commands.addCommand + editor.commands.addCommand name: "add-new-comment", bindKey: win: "Ctrl-Shift-C", mac: "Command-Shift-C" exec: (editor) => @@ -198,7 +201,7 @@ define [ scope.$watch "onCtrlShiftA", (callback) -> if callback? - editor.commands.addCommand + editor.commands.addCommand name: "toggle-track-changes", bindKey: win: "Ctrl-Shift-A", mac: "Command-Shift-A" exec: (editor) => @@ -302,7 +305,7 @@ define [ if updateCount == 100 event_tracking.send 'editor-interaction', 'multi-doc-update' scope.$emit "#{scope.name}:change" - + onScroll = (scrollTop) -> return if !scope.eventsBridge? height = editor.renderer.layerConfig.maxHeight @@ -311,7 +314,7 @@ define [ onScrollbarVisibilityChanged = (event, vRenderer) -> return if !scope.eventsBridge? scope.eventsBridge.emit "aceScrollbarVisibilityChanged", vRenderer.scrollBarV.isVisible, vRenderer.scrollBarV.width - + if scope.eventsBridge? editor.renderer.on "scrollbarVisibilityChanged", onScrollbarVisibilityChanged @@ -401,14 +404,14 @@ define [ session = editor.getSession() session.off "changeScrollTop" - + doc = session.getDocument() doc.off "change", onChange - + editor.renderer.on "changeCharacterSize", () -> scope.$apply () -> scope.rendererData.lineHeight = editor.renderer.lineHeight - + scope.$watch "rendererData", (rendererData) -> if rendererData? rendererData.lineHeight = editor.renderer.lineHeight diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee index 24a524b4b6..3306509f3c 100644 --- a/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/auto-complete/AutoCompleteManager.coffee @@ -9,7 +9,7 @@ define [ aceSnippetManager = ace.require('ace/snippets').snippetManager class AutoCompleteManager - constructor: (@$scope, @editor, @element, @labelsManager, @graphics, @preamble) -> + constructor: (@$scope, @editor, @element, @metadataManager, @graphics, @preamble) -> @suggestionManager = new CommandManager() @monkeyPatchAutocomplete() @@ -34,6 +34,8 @@ define [ enableLiveAutocompletion: false }) + # metadataManager = @metadataManager + SnippetCompleter = new EnvironmentManager() Graphics = @graphics @@ -63,7 +65,7 @@ define [ } callback null, result - labelsManager = @labelsManager + metadataManager = @metadataManager LabelsCompleter = getCompletions: (editor, session, pos, prefix, callback) -> context = Helpers.getContext(editor, pos) @@ -80,7 +82,7 @@ define [ meta: "cross-reference", score: 60 } - for label in labelsManager.getAllLabels() + for label in metadataManager.getAllLabels() result.push { caption: "\\#{commandName}{#{label}#{if needsClosingBrace then '}' else ''}", value: "\\#{commandName}{#{label}#{if needsClosingBrace then '}' else ''}", diff --git a/services/web/public/coffee/ide/editor/directives/aceEditor/metadata/MetadataManager.coffee b/services/web/public/coffee/ide/editor/directives/aceEditor/metadata/MetadataManager.coffee new file mode 100644 index 0000000000..8f104f3ace --- /dev/null +++ b/services/web/public/coffee/ide/editor/directives/aceEditor/metadata/MetadataManager.coffee @@ -0,0 +1,77 @@ +define [ + "ace/ace" +], () -> + Range = ace.require("ace/range").Range + + getLastCommandFragment = (lineUpToCursor) -> + if m = lineUpToCursor.match(/(\\[^\\]+)$/) + return m[1] + else + return null + + class MetadataManager + constructor: (@$scope, @editor, @element, @Metadata) -> + @debouncer = {} # DocId => Timeout + + onChange = (change) => + if change.remote + return + if change.action not in ['remove', 'insert'] + return + cursorPosition = @editor.getCursorPosition() + end = change.end + range = new Range(end.row, 0, end.row, end.column) + lineUpToCursor = @editor.getSession().getTextRange(range) + commandFragment = getLastCommandFragment(lineUpToCursor) + + linesContainLabel = _.any( + change.lines, + (line) -> line.match(/\\label\{[^\}\n\\]{0,80}\}/) + ) + linesContainPackage = _.any( + change.lines, + (line) -> line.match(/\\usepackage(?:\[.*?])?\s*{.*?}/) + ) + linesContainMeta = linesContainPackage or linesContainLabel + + lastCommandFragmentIsLabel = commandFragment?.startsWith '\\label{' + lastCommandFragmentIsPackage = commandFragment?.startsWith '\\usepackage' + lastCommandFragmentIsMeta = lastCommandFragmentIsPackage or lastCommandFragmentIsLabel + + if linesContainMeta or lastCommandFragmentIsMeta + @scheduleLoadCurrentDocMetadataFromServer() + + @editor.on 'changeSession', (e) => + e.oldSession.off 'change', onChange + e.session.on 'change', onChange + + loadCurrentDocMetadataFromServer: () -> + currentDocId = @$scope.docId + @Metadata.loadDocMetadataFromServer currentDocId + + loadDocMetadataFromServer: (docId) -> + @Metadata.loadDocMetadataFromServer docId + + scheduleLoadCurrentDocMetadataFromServer: () -> + # De-bounce loading labels with a timeout + currentDocId = @$scope.docId + existingTimeout = @debouncer[currentDocId] + if existingTimeout? + clearTimeout(existingTimeout) + delete @debouncer[currentDocId] + @debouncer[currentDocId] = setTimeout( + () => + @loadDocMetadataFromServer(currentDocId) + delete @debouncer[currentDocId] + , 1000 + , this + ) + + getAllLabels: () -> + @Metadata.getAllLabels() + + getAllPackages: () -> + @Metadata.getAllPackages() + + getAllMetadata: () -> + @Metadata.getAllMetadata()