mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge branch 'ns-package-aware-autocomplete'
This commit is contained in:
commit
2a50a18d23
17 changed files with 1360 additions and 375 deletions
|
@ -0,0 +1,28 @@
|
|||
EditorRealTimeController = require "../Editor/EditorRealTimeController"
|
||||
MetaHandler = require './MetaHandler'
|
||||
logger = require 'logger-sharelatex'
|
||||
|
||||
|
||||
module.exports = MetaController =
|
||||
|
||||
getMetadata: (req, res, next) ->
|
||||
project_id = req.params.project_id
|
||||
logger.log {project_id}, "getting all labels for project"
|
||||
MetaHandler.getAllMetaForProject project_id, (err, projectMeta) ->
|
||||
if err?
|
||||
logger.err {project_id, err}, "[MetaController] error getting all labels from project"
|
||||
return next err
|
||||
res.json {projectId: project_id, projectMeta: projectMeta}
|
||||
|
||||
broadcastMetadataForDoc: (req, res, next) ->
|
||||
project_id = req.params.project_id
|
||||
doc_id = req.params.doc_id
|
||||
logger.log {project_id, doc_id}, "getting labels for doc"
|
||||
MetaHandler.getMetaForDoc project_id, doc_id, (err, docMeta) ->
|
||||
if err?
|
||||
logger.err {project_id, doc_id, err}, "[MetaController] error getting labels from doc"
|
||||
return next err
|
||||
EditorRealTimeController.emitToRoom project_id, 'broadcastDocMeta', {
|
||||
docId: doc_id, meta: docMeta
|
||||
}
|
||||
res.sendStatus 200
|
66
services/web/app/coffee/Features/Metadata/MetaHandler.coffee
Normal file
66
services/web/app/coffee/Features/Metadata/MetaHandler.coffee
Normal file
|
@ -0,0 +1,66 @@
|
|||
ProjectEntityHandler = require "../Project/ProjectEntityHandler"
|
||||
DocumentUpdaterHandler = require '../DocumentUpdater/DocumentUpdaterHandler'
|
||||
packageMapping = require "./packageMapping"
|
||||
|
||||
|
||||
module.exports = MetaHandler =
|
||||
|
||||
labelRegex: () ->
|
||||
/\\label{(.{0,80}?)}/g
|
||||
|
||||
usepackageRegex: () ->
|
||||
/^\\usepackage(?:\[.{0,80}?])?{(.{0,80}?)}/g
|
||||
|
||||
ReqPackageRegex: () ->
|
||||
/^\\RequirePackage(?:\[.{0,80}?])?{(.{0,80}?)}/g
|
||||
|
||||
getAllMetaForProject: (projectId, callback=(err, projectMeta)->) ->
|
||||
DocumentUpdaterHandler.flushProjectToMongo projectId, (err) ->
|
||||
if err?
|
||||
return callback err
|
||||
ProjectEntityHandler.getAllDocs projectId, (err, docs) ->
|
||||
if err?
|
||||
return callback err
|
||||
projectMeta = MetaHandler.extractMetaFromProjectDocs docs
|
||||
callback null, projectMeta
|
||||
|
||||
getMetaForDoc: (projectId, docId, callback=(err, docMeta)->) ->
|
||||
DocumentUpdaterHandler.flushDocToMongo projectId, docId, (err) ->
|
||||
if err?
|
||||
return callback err
|
||||
ProjectEntityHandler.getDoc projectId, docId, (err, lines) ->
|
||||
if err?
|
||||
return callback err
|
||||
docMeta = MetaHandler.extractMetaFromDoc lines
|
||||
callback null, docMeta
|
||||
|
||||
extractMetaFromDoc: (lines) ->
|
||||
docMeta = {labels: [], packages: {}}
|
||||
packages = []
|
||||
label_re = MetaHandler.labelRegex()
|
||||
package_re = MetaHandler.usepackageRegex()
|
||||
req_package_re = MetaHandler.ReqPackageRegex()
|
||||
for line in lines
|
||||
while labelMatch = label_re.exec line
|
||||
if label = labelMatch[1]
|
||||
docMeta.labels.push label
|
||||
while packageMatch = package_re.exec line
|
||||
if messy = packageMatch[1]
|
||||
for pkg in messy.split ','
|
||||
if clean = pkg.trim()
|
||||
packages.push clean
|
||||
while packageMatch = req_package_re.exec line
|
||||
if messy = packageMatch[1]
|
||||
for pkg in messy.split ','
|
||||
if clean = pkg.trim()
|
||||
packages.push clean
|
||||
for pkg in packages
|
||||
if packageMapping[pkg]?
|
||||
docMeta.packages[pkg] = packageMapping[pkg]
|
||||
return docMeta
|
||||
|
||||
extractMetaFromProjectDocs: (projectDocs) ->
|
||||
projectMeta = {}
|
||||
for _path, doc of projectDocs
|
||||
projectMeta[doc._id] = MetaHandler.extractMetaFromDoc doc.lines
|
||||
return projectMeta
|
File diff suppressed because one or more lines are too long
|
@ -43,6 +43,7 @@ SudoModeController = require('./Features/SudoMode/SudoModeController')
|
|||
SudoModeMiddlewear = require('./Features/SudoMode/SudoModeMiddlewear')
|
||||
AnalyticsRouter = require('./Features/Analytics/AnalyticsRouter')
|
||||
AnnouncementsController = require("./Features/Announcements/AnnouncementsController")
|
||||
MetaController = require('./Features/Metadata/MetaController')
|
||||
LabelsController = require('./Features/Labels/LabelsController')
|
||||
TokenAccessController = require('./Features/TokenAccess/TokenAccessController')
|
||||
Features = require('./infrastructure/Features')
|
||||
|
@ -202,6 +203,9 @@ module.exports = class Router
|
|||
webRouter.get '/Project/:Project_id/download/zip', AuthorizationMiddlewear.ensureUserCanReadProject, ProjectDownloadsController.downloadProject
|
||||
webRouter.get '/project/download/zip', AuthorizationMiddlewear.ensureUserCanReadMultipleProjects, ProjectDownloadsController.downloadMultipleProjects
|
||||
|
||||
webRouter.get '/project/:project_id/metadata', AuthorizationMiddlewear.ensureUserCanReadProject, AuthenticationController.requireLogin(), MetaController.getMetadata
|
||||
webRouter.post '/project/:project_id/doc/:doc_id/metadata', AuthorizationMiddlewear.ensureUserCanReadProject, AuthenticationController.requireLogin(), MetaController.broadcastMetadataForDoc
|
||||
|
||||
webRouter.get '/project/:project_id/labels', AuthorizationMiddlewear.ensureUserCanReadProject, AuthenticationController.requireLogin(), LabelsController.getAllLabels
|
||||
webRouter.post '/project/:project_id/doc/:doc_id/labels', AuthorizationMiddlewear.ensureUserCanReadProject, AuthenticationController.requireLogin(), LabelsController.broadcastLabelsForDoc
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ define [
|
|||
"ide/pdf/PdfManager"
|
||||
"ide/binary-files/BinaryFilesManager"
|
||||
"ide/references/ReferencesManager"
|
||||
"ide/labels/LabelsManager"
|
||||
"ide/metadata/MetadataManager"
|
||||
"ide/review-panel/ReviewPanelManager"
|
||||
"ide/SafariScrollPatcher"
|
||||
"ide/AutoCompileOnboardingController",
|
||||
|
@ -47,12 +47,12 @@ define [
|
|||
PdfManager
|
||||
BinaryFilesManager
|
||||
ReferencesManager
|
||||
LabelsManager
|
||||
MetadataManager
|
||||
ReviewPanelManager
|
||||
SafariScrollPatcher
|
||||
) ->
|
||||
|
||||
App.controller "IdeController", ($scope, $timeout, ide, localStorage, sixpack, event_tracking, labels) ->
|
||||
App.controller "IdeController", ($scope, $timeout, ide, localStorage, sixpack, event_tracking, metadata) ->
|
||||
# Don't freak out if we're already in an apply callback
|
||||
$scope.$originalApply = $scope.$apply
|
||||
$scope.$apply = (fn = () ->) ->
|
||||
|
@ -72,10 +72,10 @@ define [
|
|||
view: "editor"
|
||||
chatOpen: false
|
||||
pdfLayout: 'sideBySide'
|
||||
pdfHidden: false,
|
||||
pdfWidth: 0,
|
||||
reviewPanelOpen: localStorage("ui.reviewPanelOpen.#{window.project_id}"),
|
||||
miniReviewPanelVisible: false,
|
||||
pdfHidden: false
|
||||
pdfWidth: 0
|
||||
reviewPanelOpen: localStorage("ui.reviewPanelOpen.#{window.project_id}")
|
||||
miniReviewPanelVisible: false
|
||||
}
|
||||
$scope.onboarding = {
|
||||
autoCompile: if window.showAutoCompileOnboarding then 'unseen' else 'dismissed'
|
||||
|
@ -138,7 +138,7 @@ define [
|
|||
ide.pdfManager = new PdfManager(ide, $scope)
|
||||
ide.permissionsManager = new PermissionsManager(ide, $scope)
|
||||
ide.binaryFilesManager = new BinaryFilesManager(ide, $scope)
|
||||
ide.labelsManager = new LabelsManager(ide, $scope, labels)
|
||||
ide.metadataManager = new MetadataManager(ide, $scope, metadata)
|
||||
|
||||
inited = false
|
||||
$scope.$on "project:joined", () ->
|
||||
|
@ -153,7 +153,7 @@ define [
|
|||
$timeout(
|
||||
() ->
|
||||
if $scope.permissions.write
|
||||
ide.labelsManager.loadProjectLabelsFromServer()
|
||||
ide.metadataManager.loadProjectMetaFromServer()
|
||||
_labelsInitialLoadDone = true
|
||||
, 200
|
||||
)
|
||||
|
|
|
@ -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/metadata/MetadataManager"
|
||||
"ide/editor/directives/aceEditor/labels/LabelsManager"
|
||||
"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, LabelsManager) ->
|
||||
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, $http, $q) ->
|
||||
App.directive "aceEditor", ($timeout, $compile, $rootScope, event_tracking, localStorage, $cacheFactory, labels, metadata, graphics, preamble, $http, $q) ->
|
||||
monkeyPatchSearch($rootScope, $compile)
|
||||
|
||||
return {
|
||||
|
@ -103,7 +105,8 @@ define [
|
|||
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)
|
||||
autoCompleteManager = new AutoCompleteManager(scope, editor, element, metadataManager, labelsManager, graphics, preamble)
|
||||
|
||||
|
||||
# Prevert Ctrl|Cmd-S from triggering save dialog
|
||||
|
|
|
@ -10,8 +10,7 @@ define [
|
|||
aceSnippetManager = ace.require('ace/snippets').snippetManager
|
||||
|
||||
class AutoCompleteManager
|
||||
constructor: (@$scope, @editor, @element, @labelsManager, @graphics, @preamble) ->
|
||||
@suggestionManager = new CommandManager()
|
||||
constructor: (@$scope, @editor, @element, @metadataManager, @labelsManager, @graphics, @preamble) ->
|
||||
|
||||
@monkeyPatchAutocomplete()
|
||||
|
||||
|
@ -35,6 +34,8 @@ define [
|
|||
enableLiveAutocompletion: false
|
||||
})
|
||||
|
||||
commandCompleter = new CommandManager(@metadataManager)
|
||||
|
||||
SnippetCompleter = new EnvironmentManager()
|
||||
PackageCompleter = new PackageManager()
|
||||
|
||||
|
@ -65,7 +66,7 @@ define [
|
|||
}
|
||||
callback null, result
|
||||
|
||||
labelsManager = @labelsManager
|
||||
metadataManager = @metadataManager
|
||||
LabelsCompleter =
|
||||
getCompletions: (editor, session, pos, prefix, callback) ->
|
||||
context = Helpers.getContext(editor, pos)
|
||||
|
@ -76,13 +77,14 @@ define [
|
|||
commandName = refMatch[1]
|
||||
currentArg = refMatch[2]
|
||||
result = []
|
||||
result.push {
|
||||
caption: "\\#{commandName}{}",
|
||||
snippet: "\\#{commandName}{}",
|
||||
meta: "cross-reference",
|
||||
score: 60
|
||||
}
|
||||
for label in labelsManager.getAllLabels()
|
||||
if commandName != 'ref' # ref is in top 100 commands
|
||||
result.push {
|
||||
caption: "\\#{commandName}{}",
|
||||
snippet: "\\#{commandName}{}",
|
||||
meta: "cross-reference",
|
||||
score: 60
|
||||
}
|
||||
for label in metadataManager.getAllLabels()
|
||||
result.push {
|
||||
caption: "\\#{commandName}{#{label}#{if needsClosingBrace then '}' else ''}",
|
||||
value: "\\#{commandName}{#{label}#{if needsClosingBrace then '}' else ''}",
|
||||
|
@ -128,9 +130,9 @@ define [
|
|||
callback null, result
|
||||
|
||||
@editor.completers = [
|
||||
@suggestionManager
|
||||
commandCompleter
|
||||
SnippetCompleter
|
||||
PackageCompleter
|
||||
PackageCompleter
|
||||
ReferencesCompleter
|
||||
LabelsCompleter
|
||||
GraphicsCompleter
|
||||
|
|
|
@ -1,81 +1,8 @@
|
|||
define [], () ->
|
||||
noArgumentCommands = [
|
||||
'item', 'hline', 'lipsum', 'centering', 'noindent', 'textwidth', 'draw',
|
||||
'maketitle', 'newpage', 'verb', 'bibliography', 'hfill', 'par',
|
||||
'in', 'sum', 'cdot', 'ldots', 'linewidth', 'left', 'right', 'today',
|
||||
'clearpage', 'newline', 'endinput', 'tableofcontents', 'vfill',
|
||||
'bigskip', 'fill', 'cleardoublepage', 'infty', 'leq', 'geq', 'times',
|
||||
'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'varepsilon', 'zeta',
|
||||
'eta', 'theta', 'vartheta', 'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi',
|
||||
'pi', 'varpi', 'rho', 'varrho', 'sigma', 'varsigma', 'tau', 'upsilon',
|
||||
'phi', 'varphi', 'chi', 'psi', 'omega', 'Gamma', 'Delta', 'Theta',
|
||||
'Lambda', 'Xi', 'Pi', 'Sigma', 'Upsilon', 'Phi', 'Psi', 'Omega'
|
||||
]
|
||||
singleArgumentCommands = [
|
||||
'chapter', 'section', 'label', 'textbf', 'subsection',
|
||||
'vspace', 'cite', 'textit', 'documentclass', 'includegraphics', 'input',
|
||||
'emph','caption', 'ref', 'title', 'author', 'texttt', 'include',
|
||||
'hspace', 'bibitem', 'url', 'large', 'subsubsection', 'textsc', 'date',
|
||||
'footnote', 'small', 'thanks', 'underline', 'graphicspath', 'pageref',
|
||||
'section*', 'subsection*', 'subsubsection*', 'sqrt', 'text',
|
||||
'normalsize', 'footnotesize', 'Large', 'paragraph', 'pagestyle',
|
||||
'thispagestyle', 'bibliographystyle', 'hat'
|
||||
]
|
||||
doubleArgumentCommands = [
|
||||
'newcommand', 'frac', 'dfrac', 'renewcommand', 'setlength', 'href',
|
||||
'newtheorem'
|
||||
]
|
||||
tripleArgumentCommands = [
|
||||
'addcontentsline', 'newacronym', 'multicolumn'
|
||||
]
|
||||
special = ['LaTeX', 'TeX']
|
||||
define [
|
||||
"./top_hundred_snippets"
|
||||
], (topHundred) ->
|
||||
|
||||
rawCommands = ['usepackage'].concat(
|
||||
noArgumentCommands,
|
||||
singleArgumentCommands,
|
||||
doubleArgumentCommands,
|
||||
tripleArgumentCommands,
|
||||
special
|
||||
)
|
||||
|
||||
noArgumentCommands = for cmd in noArgumentCommands
|
||||
{
|
||||
caption: "\\#{cmd}"
|
||||
snippet: "\\#{cmd}"
|
||||
meta: "cmd"
|
||||
}
|
||||
singleArgumentCommands = for cmd in singleArgumentCommands
|
||||
{
|
||||
caption: "\\#{cmd}{}"
|
||||
snippet: "\\#{cmd}{$1}"
|
||||
meta: "cmd"
|
||||
}
|
||||
doubleArgumentCommands = for cmd in doubleArgumentCommands
|
||||
{
|
||||
caption: "\\#{cmd}{}{}"
|
||||
snippet: "\\#{cmd}{$1}{$2}"
|
||||
meta: "cmd"
|
||||
}
|
||||
tripleArgumentCommands = for cmd in tripleArgumentCommands
|
||||
{
|
||||
caption: "\\#{cmd}{}{}{}"
|
||||
snippet: "\\#{cmd}{$1}{$2}{$3}"
|
||||
meta: "cmd"
|
||||
}
|
||||
special = for cmd in special
|
||||
{
|
||||
caption: "\\#{cmd}{}"
|
||||
snippet: "\\#{cmd}{}"
|
||||
meta: "cmd"
|
||||
}
|
||||
|
||||
staticCommands = [].concat(
|
||||
noArgumentCommands,
|
||||
singleArgumentCommands,
|
||||
doubleArgumentCommands,
|
||||
tripleArgumentCommands,
|
||||
special
|
||||
)
|
||||
commandNames = (snippet.caption.match(/\w+/)[0] for snippet in topHundred)
|
||||
|
||||
class Parser
|
||||
constructor: (@doc, @prefix) ->
|
||||
|
@ -129,10 +56,10 @@ define [], () ->
|
|||
return realCommands
|
||||
|
||||
# Ignore single letter commands since auto complete is moot then.
|
||||
commandRegex: /\\([a-zA-Z][a-zA-Z]+)/
|
||||
commandRegex: /\\([a-zA-Z]{2,})/
|
||||
|
||||
nextCommand: () ->
|
||||
i = @doc.search(@commandRegex)
|
||||
i = @doc.search @commandRegex
|
||||
if i == -1
|
||||
return false
|
||||
else
|
||||
|
@ -166,13 +93,21 @@ define [], () ->
|
|||
return false
|
||||
|
||||
class CommandManager
|
||||
constructor: (@metadataManager) ->
|
||||
|
||||
getCompletions: (editor, session, pos, prefix, callback) ->
|
||||
packages = @metadataManager.getAllPackages()
|
||||
packageCommands = []
|
||||
for pkg, snippets of packages
|
||||
for snippet in snippets
|
||||
packageCommands.push snippet
|
||||
|
||||
doc = session.getValue()
|
||||
parser = new Parser(doc, prefix)
|
||||
commands = parser.parse()
|
||||
completions = []
|
||||
for command in commands
|
||||
if command[0] not in rawCommands
|
||||
if command[0] not in commandNames
|
||||
caption = "\\#{command[0]}"
|
||||
score = if caption == prefix then 99 else 50
|
||||
snippet = caption
|
||||
|
@ -191,9 +126,9 @@ define [], () ->
|
|||
meta: "cmd"
|
||||
score: score
|
||||
}
|
||||
completions = completions.concat staticCommands
|
||||
completions = completions.concat topHundred, packageCommands
|
||||
|
||||
callback(null, completions)
|
||||
callback null, completions
|
||||
|
||||
loadCommandsFromDoc: (doc) ->
|
||||
parser = new Parser(doc)
|
||||
|
|
|
@ -0,0 +1,691 @@
|
|||
define -> [{
|
||||
"caption": "\\begin{}",
|
||||
"snippet": "\\begin{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 7.849662248028187
|
||||
}, {
|
||||
"caption": "\\begin{}[]",
|
||||
"snippet": "\\begin{$1}[$2]",
|
||||
"meta": "cmd",
|
||||
"score": 7.849662248028187
|
||||
}, {
|
||||
"caption": "\\begin{}{}",
|
||||
"snippet": "\\begin{$1}{$2}",
|
||||
"meta": "cmd",
|
||||
"score": 7.849662248028187
|
||||
}, {
|
||||
"caption": "\\end{}",
|
||||
"snippet": "\\end{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 7.847906405228455
|
||||
}, {
|
||||
"caption": "\\usepackage{}",
|
||||
"snippet": "\\usepackage{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 5.427890758130527
|
||||
}, {
|
||||
"caption": "\\usepackage[]{}",
|
||||
"snippet": "\\usepackage[$1]{$2}",
|
||||
"meta": "cmd",
|
||||
"score": 5.427890758130527
|
||||
}, {
|
||||
"caption": "\\item",
|
||||
"snippet": "\\item",
|
||||
"meta": "cmd",
|
||||
"score": 3.800886892251021
|
||||
}, {
|
||||
"caption": "\\item[]",
|
||||
"snippet": "\\item[$1]",
|
||||
"meta": "cmd",
|
||||
"score": 3.800886892251021
|
||||
}, {
|
||||
"caption": "\\section{}",
|
||||
"snippet": "\\section{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 3.0952612541683835
|
||||
}, {
|
||||
"caption": "\\textbf{}",
|
||||
"snippet": "\\textbf{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 2.627755982816738
|
||||
}, {
|
||||
"caption": "\\cite{}",
|
||||
"snippet": "\\cite{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 2.341195220791228
|
||||
}, {
|
||||
"caption": "\\label{}",
|
||||
"snippet": "\\label{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 1.897791904799601
|
||||
}, {
|
||||
"caption": "\\textit{}",
|
||||
"snippet": "\\textit{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 1.6842996195493385
|
||||
}, {
|
||||
"caption": "\\includegraphics[]{}",
|
||||
"snippet": "\\includegraphics[$1]{$2}",
|
||||
"meta": "cmd",
|
||||
"score": 1.4595731795525781
|
||||
}, {
|
||||
"caption": "\\documentclass[]{}",
|
||||
"snippet": "\\documentclass[$1]{$2}",
|
||||
"meta": "cmd",
|
||||
"score": 1.4425339817971206
|
||||
}, {
|
||||
"caption": "\\documentclass{}",
|
||||
"snippet": "\\documentclass{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 1.4425339817971206
|
||||
}, {
|
||||
"caption": "\\frac{}{}",
|
||||
"snippet": "\\frac{$1}{$2}",
|
||||
"meta": "cmd",
|
||||
"score": 1.4341091141105058
|
||||
}, {
|
||||
"caption": "\\subsection{}",
|
||||
"snippet": "\\subsection{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 1.3890912739512353
|
||||
}, {
|
||||
"caption": "\\hline",
|
||||
"snippet": "\\hline",
|
||||
"meta": "cmd",
|
||||
"score": 1.3209538327406387
|
||||
}, {
|
||||
"caption": "\\caption{}",
|
||||
"snippet": "\\caption{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 1.2569477427490174
|
||||
}, {
|
||||
"caption": "\\centering",
|
||||
"snippet": "\\centering",
|
||||
"meta": "cmd",
|
||||
"score": 1.1642881814937829
|
||||
}, {
|
||||
"caption": "\\vspace{}",
|
||||
"snippet": "\\vspace{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.9533807826673939
|
||||
}, {
|
||||
"caption": "\\title{}",
|
||||
"snippet": "\\title{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.9202908262245683
|
||||
}, {
|
||||
"caption": "\\author{}",
|
||||
"snippet": "\\author{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.8973590434087177
|
||||
}, {
|
||||
"caption": "\\author[]{}",
|
||||
"snippet": "\\author[$1]{$2}",
|
||||
"meta": "cmd",
|
||||
"score": 0.8973590434087177
|
||||
}, {
|
||||
"caption": "\\maketitle",
|
||||
"snippet": "\\maketitle",
|
||||
"meta": "cmd",
|
||||
"score": 0.7504160124360846
|
||||
}, {
|
||||
"caption": "\\textwidth",
|
||||
"snippet": "\\textwidth",
|
||||
"meta": "cmd",
|
||||
"score": 0.7355328080889112
|
||||
}, {
|
||||
"caption": "\\newcommand{}{}",
|
||||
"snippet": "\\newcommand{$1}{$2}",
|
||||
"meta": "cmd",
|
||||
"score": 0.7264891987129375
|
||||
}, {
|
||||
"caption": "\\newcommand{}[]{}",
|
||||
"snippet": "\\newcommand{$1}[$2]{$3}",
|
||||
"meta": "cmd",
|
||||
"score": 0.7264891987129375
|
||||
}, {
|
||||
"caption": "\\date{}",
|
||||
"snippet": "\\date{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.7225518453076786
|
||||
}, {
|
||||
"caption": "\\emph{}",
|
||||
"snippet": "\\emph{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.7060308784832261
|
||||
}, {
|
||||
"caption": "\\textsc{}",
|
||||
"snippet": "\\textsc{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.6926466355384758
|
||||
}, {
|
||||
"caption": "\\multicolumn{}{}{}",
|
||||
"snippet": "\\multicolumn{$1}{$2}{$3}",
|
||||
"meta": "cmd",
|
||||
"score": 0.5473606021405326
|
||||
}, {
|
||||
"caption": "\\input{}",
|
||||
"snippet": "\\input{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.4966021927742672
|
||||
}, {
|
||||
"caption": "\\alpha",
|
||||
"snippet": "\\alpha",
|
||||
"meta": "cmd",
|
||||
"score": 0.49520006391384913
|
||||
}, {
|
||||
"caption": "\\in",
|
||||
"snippet": "\\in",
|
||||
"meta": "cmd",
|
||||
"score": 0.4716039670146658
|
||||
}, {
|
||||
"caption": "\\mathbf{}",
|
||||
"snippet": "\\mathbf{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.4682018419466319
|
||||
}, {
|
||||
"caption": "\\right",
|
||||
"snippet": "\\right",
|
||||
"meta": "cmd",
|
||||
"score": 0.4299239459457309
|
||||
}, {
|
||||
"caption": "\\left",
|
||||
"snippet": "\\left",
|
||||
"meta": "cmd",
|
||||
"score": 0.42937815279867964
|
||||
}, {
|
||||
"caption": "\\left[]",
|
||||
"snippet": "\\left[$1]",
|
||||
"meta": "cmd",
|
||||
"score": 0.42937815279867964
|
||||
}, {
|
||||
"caption": "\\sum",
|
||||
"snippet": "\\sum",
|
||||
"meta": "cmd",
|
||||
"score": 0.42607994509619934
|
||||
}, {
|
||||
"caption": "\\noindent",
|
||||
"snippet": "\\noindent",
|
||||
"meta": "cmd",
|
||||
"score": 0.42355747798114207
|
||||
}, {
|
||||
"caption": "\\chapter{}",
|
||||
"snippet": "\\chapter{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.422097569591803
|
||||
}, {
|
||||
"caption": "\\par",
|
||||
"snippet": "\\par",
|
||||
"meta": "cmd",
|
||||
"score": 0.413853376001159
|
||||
}, {
|
||||
"caption": "\\lambda",
|
||||
"snippet": "\\lambda",
|
||||
"meta": "cmd",
|
||||
"score": 0.39389600578684125
|
||||
}, {
|
||||
"caption": "\\subsubsection{}",
|
||||
"snippet": "\\subsubsection{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.3727781330132016
|
||||
}, {
|
||||
"caption": "\\bibitem{}",
|
||||
"snippet": "\\bibitem{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.3689547570562042
|
||||
}, {
|
||||
"caption": "\\bibitem[]{}",
|
||||
"snippet": "\\bibitem[$1]{$2}",
|
||||
"meta": "cmd",
|
||||
"score": 0.3689547570562042
|
||||
}, {
|
||||
"caption": "\\text{}",
|
||||
"snippet": "\\text{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.3608680734736821
|
||||
}, {
|
||||
"caption": "\\setlength{}{}",
|
||||
"snippet": "\\setlength{$1}{$2}",
|
||||
"meta": "cmd",
|
||||
"score": 0.354445763583904
|
||||
}, {
|
||||
"caption": "\\setlength",
|
||||
"snippet": "\\setlength",
|
||||
"meta": "cmd",
|
||||
"score": 0.354445763583904
|
||||
}, {
|
||||
"caption": "\\mathcal{}",
|
||||
"snippet": "\\mathcal{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.35084018920966636
|
||||
}, {
|
||||
"caption": "\\newline",
|
||||
"snippet": "\\newline",
|
||||
"meta": "cmd",
|
||||
"score": 0.3311721696201715
|
||||
}, {
|
||||
"caption": "\\newpage",
|
||||
"snippet": "\\newpage",
|
||||
"meta": "cmd",
|
||||
"score": 0.3277033727934986
|
||||
}, {
|
||||
"caption": "\\renewcommand{}{}",
|
||||
"snippet": "\\renewcommand{$1}{$2}",
|
||||
"meta": "cmd",
|
||||
"score": 0.3267437011085663
|
||||
}, {
|
||||
"caption": "\\renewcommand",
|
||||
"snippet": "\\renewcommand",
|
||||
"meta": "cmd",
|
||||
"score": 0.3267437011085663
|
||||
}, {
|
||||
"caption": "\\theta",
|
||||
"snippet": "\\theta",
|
||||
"meta": "cmd",
|
||||
"score": 0.3210417159232142
|
||||
}, {
|
||||
"caption": "\\hspace{}",
|
||||
"snippet": "\\hspace{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.3147206476372336
|
||||
}, {
|
||||
"caption": "\\beta",
|
||||
"snippet": "\\beta",
|
||||
"meta": "cmd",
|
||||
"score": 0.3061799530337638
|
||||
}, {
|
||||
"caption": "\\texttt{}",
|
||||
"snippet": "\\texttt{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.3019066753744355
|
||||
}, {
|
||||
"caption": "\\times",
|
||||
"snippet": "\\times",
|
||||
"meta": "cmd",
|
||||
"score": 0.2957960629411553
|
||||
}, {
|
||||
"caption": "\\citep{}",
|
||||
"snippet": "\\citep{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.2941882834697057
|
||||
}, {
|
||||
"caption": "\\color[]{}",
|
||||
"snippet": "\\color[$1]{$2}",
|
||||
"meta": "cmd",
|
||||
"score": 0.2864294797053033
|
||||
}, {
|
||||
"caption": "\\color{}",
|
||||
"snippet": "\\color{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.2864294797053033
|
||||
}, {
|
||||
"caption": "\\mu",
|
||||
"snippet": "\\mu",
|
||||
"meta": "cmd",
|
||||
"score": 0.27635652476799255
|
||||
}, {
|
||||
"caption": "\\bibliography{}",
|
||||
"snippet": "\\bibliography{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.2659628337907604
|
||||
}, {
|
||||
"caption": "\\linewidth",
|
||||
"snippet": "\\linewidth",
|
||||
"meta": "cmd",
|
||||
"score": 0.2639498312518439
|
||||
}, {
|
||||
"caption": "\\delta",
|
||||
"snippet": "\\delta",
|
||||
"meta": "cmd",
|
||||
"score": 0.2620578600722735
|
||||
}, {
|
||||
"caption": "\\sigma",
|
||||
"snippet": "\\sigma",
|
||||
"meta": "cmd",
|
||||
"score": 0.25940147926344487
|
||||
}, {
|
||||
"caption": "\\pi",
|
||||
"snippet": "\\pi",
|
||||
"meta": "cmd",
|
||||
"score": 0.25920934567729714
|
||||
}, {
|
||||
"caption": "\\hat{}",
|
||||
"snippet": "\\hat{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.25264309033778715
|
||||
}, {
|
||||
"caption": "\\hat",
|
||||
"snippet": "\\hat",
|
||||
"meta": "cmd",
|
||||
"score": 0.25264309033778715
|
||||
}, {
|
||||
"caption": "\\bibliographystyle{}",
|
||||
"snippet": "\\bibliographystyle{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.25122317941387773
|
||||
}, {
|
||||
"caption": "\\small",
|
||||
"snippet": "\\small",
|
||||
"meta": "cmd",
|
||||
"score": 0.2447632045426295
|
||||
}, {
|
||||
"caption": "\\small{}",
|
||||
"snippet": "\\small{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.2447632045426295
|
||||
}, {
|
||||
"caption": "\\LaTeX",
|
||||
"snippet": "\\LaTeX",
|
||||
"meta": "cmd",
|
||||
"score": 0.2334089308452787
|
||||
}, {
|
||||
"caption": "\\LaTeX{}",
|
||||
"snippet": "\\LaTeX{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.2334089308452787
|
||||
}, {
|
||||
"caption": "\\cdot",
|
||||
"snippet": "\\cdot",
|
||||
"meta": "cmd",
|
||||
"score": 0.23029085545522762
|
||||
}, {
|
||||
"caption": "\\footnote{}",
|
||||
"snippet": "\\footnote{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.2253056071787701
|
||||
}, {
|
||||
"caption": "\\newtheorem{}[]{}",
|
||||
"snippet": "\\newtheorem{$1}[$2]{$3}",
|
||||
"meta": "cmd",
|
||||
"score": 0.215689795055434
|
||||
}, {
|
||||
"caption": "\\newtheorem{}{}",
|
||||
"snippet": "\\newtheorem{$1}{$2}",
|
||||
"meta": "cmd",
|
||||
"score": 0.215689795055434
|
||||
}, {
|
||||
"caption": "\\newtheorem{}{}[]",
|
||||
"snippet": "\\newtheorem{$1}{$2}[$3]",
|
||||
"meta": "cmd",
|
||||
"score": 0.215689795055434
|
||||
}, {
|
||||
"caption": "\\Delta",
|
||||
"snippet": "\\Delta",
|
||||
"meta": "cmd",
|
||||
"score": 0.21386475063892618
|
||||
}, {
|
||||
"caption": "\\tau",
|
||||
"snippet": "\\tau",
|
||||
"meta": "cmd",
|
||||
"score": 0.21236188205859796
|
||||
}, {
|
||||
"caption": "\\hfill",
|
||||
"snippet": "\\hfill",
|
||||
"meta": "cmd",
|
||||
"score": 0.2058248088519886
|
||||
}, {
|
||||
"caption": "\\leq",
|
||||
"snippet": "\\leq",
|
||||
"meta": "cmd",
|
||||
"score": 0.20498894440637172
|
||||
}, {
|
||||
"caption": "\\footnotesize",
|
||||
"snippet": "\\footnotesize",
|
||||
"meta": "cmd",
|
||||
"score": 0.2038592081252624
|
||||
}, {
|
||||
"caption": "\\footnotesize{}",
|
||||
"snippet": "\\footnotesize{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.2038592081252624
|
||||
}, {
|
||||
"caption": "\\large",
|
||||
"snippet": "\\large",
|
||||
"meta": "cmd",
|
||||
"score": 0.20377416734108866
|
||||
}, {
|
||||
"caption": "\\large{}",
|
||||
"snippet": "\\large{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.20377416734108866
|
||||
}, {
|
||||
"caption": "\\sqrt{}",
|
||||
"snippet": "\\sqrt{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.20240160977404634
|
||||
}, {
|
||||
"caption": "\\epsilon",
|
||||
"snippet": "\\epsilon",
|
||||
"meta": "cmd",
|
||||
"score": 0.2005136761359043
|
||||
}, {
|
||||
"caption": "\\Large",
|
||||
"snippet": "\\Large",
|
||||
"meta": "cmd",
|
||||
"score": 0.1987771081149759
|
||||
}, {
|
||||
"caption": "\\Large{}",
|
||||
"snippet": "\\Large{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.1987771081149759
|
||||
}, {
|
||||
"caption": "\\cvitem{}{}",
|
||||
"snippet": "\\cvitem{$1}{$2}",
|
||||
"meta": "cmd",
|
||||
"score": 0.19605476980016281
|
||||
}, {
|
||||
"caption": "\\rho",
|
||||
"snippet": "\\rho",
|
||||
"meta": "cmd",
|
||||
"score": 0.1959287380541684
|
||||
}, {
|
||||
"caption": "\\omega",
|
||||
"snippet": "\\omega",
|
||||
"meta": "cmd",
|
||||
"score": 0.19326783415115262
|
||||
}, {
|
||||
"caption": "\\mathrm{}",
|
||||
"snippet": "\\mathrm{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.19117752976172653
|
||||
}, {
|
||||
"caption": "\\boldsymbol{}",
|
||||
"snippet": "\\boldsymbol{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.18137737738638837
|
||||
}, {
|
||||
"caption": "\\boldsymbol",
|
||||
"snippet": "\\boldsymbol",
|
||||
"meta": "cmd",
|
||||
"score": 0.18137737738638837
|
||||
}, {
|
||||
"caption": "\\gamma",
|
||||
"snippet": "\\gamma",
|
||||
"meta": "cmd",
|
||||
"score": 0.17940276535431304
|
||||
}, {
|
||||
"caption": "\\clearpage",
|
||||
"snippet": "\\clearpage",
|
||||
"meta": "cmd",
|
||||
"score": 0.1789117552185788
|
||||
}, {
|
||||
"caption": "\\infty",
|
||||
"snippet": "\\infty",
|
||||
"meta": "cmd",
|
||||
"score": 0.17837290019711305
|
||||
}, {
|
||||
"caption": "\\phi",
|
||||
"snippet": "\\phi",
|
||||
"meta": "cmd",
|
||||
"score": 0.17405809173097808
|
||||
}, {
|
||||
"caption": "\\partial",
|
||||
"snippet": "\\partial",
|
||||
"meta": "cmd",
|
||||
"score": 0.17168102367966637
|
||||
}, {
|
||||
"caption": "\\include{}",
|
||||
"snippet": "\\include{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.1547080054979312
|
||||
}, {
|
||||
"caption": "\\address{}",
|
||||
"snippet": "\\address{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.1525055392611109
|
||||
}, {
|
||||
"caption": "\\address[]{}",
|
||||
"snippet": "\\address[$1]{$2}",
|
||||
"meta": "cmd",
|
||||
"score": 0.1525055392611109
|
||||
}, {
|
||||
"caption": "\\address{}{}{}",
|
||||
"snippet": "\\address{$1}{$2}{$3}",
|
||||
"meta": "cmd",
|
||||
"score": 0.1525055392611109
|
||||
}, {
|
||||
"caption": "\\quad",
|
||||
"snippet": "\\quad",
|
||||
"meta": "cmd",
|
||||
"score": 0.15242755832392743
|
||||
}, {
|
||||
"caption": "\\email{}",
|
||||
"snippet": "\\email{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.1522294670109857
|
||||
}, {
|
||||
"caption": "\\paragraph{}",
|
||||
"snippet": "\\paragraph{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.152074250347974
|
||||
}, {
|
||||
"caption": "\\varepsilon",
|
||||
"snippet": "\\varepsilon",
|
||||
"meta": "cmd",
|
||||
"score": 0.05411564201390573
|
||||
}, {
|
||||
"caption": "\\zeta",
|
||||
"snippet": "\\zeta",
|
||||
"meta": "cmd",
|
||||
"score": 0.023330249803752954
|
||||
}, {
|
||||
"caption": "\\eta",
|
||||
"snippet": "\\eta",
|
||||
"meta": "cmd",
|
||||
"score": 0.11088718379889091
|
||||
}, {
|
||||
"caption": "\\vartheta",
|
||||
"snippet": "\\vartheta",
|
||||
"meta": "cmd",
|
||||
"score": 0.0025822992078068712
|
||||
}, {
|
||||
"caption": "\\iota",
|
||||
"snippet": "\\iota",
|
||||
"meta": "cmd",
|
||||
"score": 0.0024774003791525486
|
||||
}, {
|
||||
"caption": "\\kappa",
|
||||
"snippet": "\\kappa",
|
||||
"meta": "cmd",
|
||||
"score": 0.04887876299369008
|
||||
}, {
|
||||
"caption": "\\nu",
|
||||
"snippet": "\\nu",
|
||||
"meta": "cmd",
|
||||
"score": 0.09206962821059342
|
||||
}, {
|
||||
"caption": "\\xi",
|
||||
"snippet": "\\xi",
|
||||
"meta": "cmd",
|
||||
"score": 0.06496042899265699
|
||||
}, {
|
||||
"caption": "\\varpi",
|
||||
"snippet": "\\varpi",
|
||||
"meta": "cmd",
|
||||
"score": 0.0007039358167790341
|
||||
}, {
|
||||
"caption": "\\varrho",
|
||||
"snippet": "\\varrho",
|
||||
"meta": "cmd",
|
||||
"score": 0.0011279491613898612
|
||||
}, {
|
||||
"caption": "\\varsigma",
|
||||
"snippet": "\\varsigma",
|
||||
"meta": "cmd",
|
||||
"score": 0.0010424880711234978
|
||||
}, {
|
||||
"caption": "\\varsigma{}",
|
||||
"snippet": "\\varsigma{$1}",
|
||||
"meta": "cmd",
|
||||
"score": 0.0010424880711234978
|
||||
}, {
|
||||
"caption": "\\upsilon",
|
||||
"snippet": "\\upsilon",
|
||||
"meta": "cmd",
|
||||
"score": 0.00420715572598688
|
||||
}, {
|
||||
"caption": "\\varphi",
|
||||
"snippet": "\\varphi",
|
||||
"meta": "cmd",
|
||||
"score": 0.03351251516668212
|
||||
}, {
|
||||
"caption": "\\chi",
|
||||
"snippet": "\\chi",
|
||||
"meta": "cmd",
|
||||
"score": 0.043373492287805675
|
||||
}, {
|
||||
"caption": "\\psi",
|
||||
"snippet": "\\psi",
|
||||
"meta": "cmd",
|
||||
"score": 0.09994508706163642
|
||||
}, {
|
||||
"caption": "\\Gamma",
|
||||
"snippet": "\\Gamma",
|
||||
"meta": "cmd",
|
||||
"score": 0.04801549269801977
|
||||
}, {
|
||||
"caption": "\\Theta",
|
||||
"snippet": "\\Theta",
|
||||
"meta": "cmd",
|
||||
"score": 0.038090902146599444
|
||||
}, {
|
||||
"caption": "\\Lambda",
|
||||
"snippet": "\\Lambda",
|
||||
"meta": "cmd",
|
||||
"score": 0.032206594305977686
|
||||
}, {
|
||||
"caption": "\\Xi",
|
||||
"snippet": "\\Xi",
|
||||
"meta": "cmd",
|
||||
"score": 0.01060997225400494
|
||||
}, {
|
||||
"caption": "\\Pi",
|
||||
"snippet": "\\Pi",
|
||||
"meta": "cmd",
|
||||
"score": 0.021264671817473237
|
||||
}, {
|
||||
"caption": "\\Sigma",
|
||||
"snippet": "\\Sigma",
|
||||
"meta": "cmd",
|
||||
"score": 0.05769642802079917
|
||||
}, {
|
||||
"caption": "\\Upsilon",
|
||||
"snippet": "\\Upsilon",
|
||||
"meta": "cmd",
|
||||
"score": 0.00032875192955749566
|
||||
}, {
|
||||
"caption": "\\Phi",
|
||||
"snippet": "\\Phi",
|
||||
"meta": "cmd",
|
||||
"score": 0.0538724950042562
|
||||
}, {
|
||||
"caption": "\\Psi",
|
||||
"snippet": "\\Psi",
|
||||
"meta": "cmd",
|
||||
"score": 0.03056589143021648
|
||||
}, {
|
||||
"caption": "\\Omega",
|
||||
"snippet": "\\Omega",
|
||||
"meta": "cmd",
|
||||
"score": 0.09490387997853639
|
||||
}]
|
|
@ -0,0 +1,85 @@
|
|||
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
|
||||
if lineUpToCursor.trim() == '%' or lineUpToCursor.startsWith '\\'
|
||||
range = new Range(end.row, 0, end.row, end.column + 80)
|
||||
lineUpToCursor = @editor.getSession().getTextRange range
|
||||
commandFragment = getLastCommandFragment lineUpToCursor
|
||||
|
||||
linesContainPackage = _.any(
|
||||
change.lines,
|
||||
(line) -> line.match(/^\\usepackage(?:\[.{0,80}?])?{(.{0,80}?)}/)
|
||||
)
|
||||
linesContainReqPackage = _.any(
|
||||
change.lines,
|
||||
(line) -> line.match(/^\\RequirePackage(?:\[.{0,80}?])?{(.{0,80}?)}/)
|
||||
)
|
||||
linesContainLabel = _.any(
|
||||
change.lines,
|
||||
(line) -> line.match(/\\label{(.{0,80}?)}/)
|
||||
)
|
||||
linesContainMeta =
|
||||
linesContainPackage or
|
||||
linesContainLabel or
|
||||
linesContainReqPackage
|
||||
|
||||
lastCommandFragmentIsLabel = commandFragment?.startsWith '\\label{'
|
||||
lastCommandFragmentIsPackage = commandFragment?.startsWith '\\usepackage'
|
||||
lastCommandFragmentIsReqPack = commandFragment?.startsWith '\\RequirePackage'
|
||||
lastCommandFragmentIsMeta =
|
||||
lastCommandFragmentIsPackage or
|
||||
lastCommandFragmentIsLabel or
|
||||
lastCommandFragmentIsReqPack
|
||||
|
||||
if linesContainMeta or lastCommandFragmentIsMeta
|
||||
@scheduleLoadCurrentDocMetaFromServer()
|
||||
|
||||
@editor.on "changeSession", (e) =>
|
||||
e.oldSession.off "change", onChange
|
||||
e.session.on "change", onChange
|
||||
|
||||
|
||||
loadDocMetaFromServer: (docId) ->
|
||||
@Metadata.loadDocMetaFromServer docId
|
||||
|
||||
scheduleLoadCurrentDocMetaFromServer: () ->
|
||||
# De-bounce loading labels with a timeout
|
||||
currentDocId = @$scope.docId
|
||||
existingTimeout = @debouncer[currentDocId]
|
||||
if existingTimeout?
|
||||
clearTimeout(existingTimeout)
|
||||
delete @debouncer[currentDocId]
|
||||
@debouncer[currentDocId] = setTimeout(
|
||||
() =>
|
||||
@loadDocMetaFromServer currentDocId
|
||||
delete @debouncer[currentDocId]
|
||||
, 1000
|
||||
, this
|
||||
)
|
||||
|
||||
getAllLabels: () ->
|
||||
@Metadata.getAllLabels()
|
||||
|
||||
getAllPackages: () ->
|
||||
@Metadata.getAllPackages()
|
|
@ -1,5 +1,4 @@
|
|||
define [
|
||||
], () ->
|
||||
define [], () ->
|
||||
|
||||
class LabelsManager
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
define [], () ->
|
||||
|
||||
class MetadataManager
|
||||
|
||||
constructor: (@ide, @$scope, @metadata) ->
|
||||
|
||||
@ide.socket.on 'broadcastDocMeta', (data) =>
|
||||
@metadata.onBroadcastDocMeta data
|
||||
@$scope.$on 'entity:deleted', @metadata.onEntityDeleted
|
||||
@$scope.$on 'file:upload:complete', @metadata.fileUploadComplete
|
||||
|
||||
loadProjectMetaFromServer: () ->
|
||||
@metadata.loadProjectMetaFromServer()
|
|
@ -0,0 +1,49 @@
|
|||
define [
|
||||
"base"
|
||||
], (App) ->
|
||||
|
||||
App.factory 'metadata', ($http, ide) ->
|
||||
|
||||
state = {documents: {}}
|
||||
|
||||
metadata = {state: state}
|
||||
|
||||
metadata.onBroadcastDocMeta = (data) ->
|
||||
if data.docId? and data.meta?
|
||||
state.documents[data.docId] = data.meta
|
||||
|
||||
metadata.onEntityDeleted = (e, entity) ->
|
||||
if entity.type == 'doc'
|
||||
delete state.documents[entity.id]
|
||||
|
||||
metadata.onFileUploadComplete = (e, upload) ->
|
||||
if upload.entity_type == 'doc'
|
||||
metadata.loadDocMetaFromServer upload.entity_id
|
||||
|
||||
metadata.getAllLabels = () ->
|
||||
_.flatten(meta.labels for docId, meta of state.documents)
|
||||
|
||||
metadata.getAllPackages = () ->
|
||||
packageCommandMapping = {}
|
||||
for _docId, meta of state.documents
|
||||
for packageName, commandSnippets of meta.packages
|
||||
packageCommandMapping[packageName] = commandSnippets
|
||||
return packageCommandMapping
|
||||
|
||||
metadata.loadProjectMetaFromServer = () ->
|
||||
$http
|
||||
.get("/project/#{window.project_id}/metadata")
|
||||
.then (response) ->
|
||||
{ data } = response
|
||||
if data.projectMeta
|
||||
for docId, docMeta of data.projectMeta
|
||||
state.documents[docId] = docMeta
|
||||
|
||||
metadata.loadDocMetaFromServer = (docId) ->
|
||||
$http
|
||||
.post(
|
||||
"/project/#{window.project_id}/doc/#{docId}/metadata",
|
||||
{_csrf: window.csrfToken}
|
||||
)
|
||||
|
||||
return metadata
|
|
@ -1,119 +0,0 @@
|
|||
chai = require('chai')
|
||||
chai.should()
|
||||
expect = chai.expect
|
||||
sinon = require("sinon")
|
||||
modulePath = "../../../../app/js/Features/Labels/LabelsController"
|
||||
SandboxedModule = require('sandboxed-module')
|
||||
|
||||
|
||||
describe 'LabelsController', ->
|
||||
beforeEach ->
|
||||
@projectId = 'somekindofid'
|
||||
@EditorRealTimeController = {
|
||||
emitToRoom: sinon.stub()
|
||||
}
|
||||
@LabelsHandler = {
|
||||
getAllLabelsForProject: sinon.stub()
|
||||
getLabelsForDoc: sinon.stub()
|
||||
}
|
||||
@LabelsController = SandboxedModule.require modulePath, requires:
|
||||
'logger-sharelatex': {log: sinon.stub(), err: sinon.stub()}
|
||||
'../Editor/EditorRealTimeController': @EditorRealTimeController
|
||||
'./LabelsHandler': @LabelsHandler
|
||||
|
||||
describe 'getAllLabels', ->
|
||||
beforeEach ->
|
||||
@fakeLabels = {'somedoc': ['a_label']}
|
||||
@LabelsHandler.getAllLabelsForProject = sinon.stub().callsArgWith(1, null, @fakeLabels)
|
||||
@req = {params: {project_id: @projectId}}
|
||||
@res = {json: sinon.stub()}
|
||||
@next = sinon.stub()
|
||||
|
||||
it 'should call LabelsHandler.getAllLabelsForProject', () ->
|
||||
@LabelsController.getAllLabels(@req, @res, @next)
|
||||
@LabelsHandler.getAllLabelsForProject.callCount.should.equal 1
|
||||
@LabelsHandler.getAllLabelsForProject.calledWith(@projectId).should.equal true
|
||||
|
||||
it 'should call not call next with an error', () ->
|
||||
@LabelsController.getAllLabels(@req, @res, @next)
|
||||
@next.callCount.should.equal 0
|
||||
|
||||
it 'should send a json response', () ->
|
||||
@LabelsController.getAllLabels(@req, @res, @next)
|
||||
@res.json.callCount.should.equal 1
|
||||
expect(@res.json.lastCall.args[0]).to.have.all.keys ['projectId', 'projectLabels']
|
||||
|
||||
describe 'when LabelsHandler.getAllLabelsForProject produces an error', ->
|
||||
beforeEach ->
|
||||
@LabelsHandler.getAllLabelsForProject = sinon.stub().callsArgWith(1, new Error('woops'))
|
||||
@req = {params: {project_id: @projectId}}
|
||||
@res = {json: sinon.stub()}
|
||||
@next = sinon.stub()
|
||||
|
||||
it 'should call LabelsHandler.getAllLabelsForProject', () ->
|
||||
@LabelsController.getAllLabels(@req, @res, @next)
|
||||
@LabelsHandler.getAllLabelsForProject.callCount.should.equal 1
|
||||
@LabelsHandler.getAllLabelsForProject.calledWith(@projectId).should.equal true
|
||||
|
||||
it 'should call next with an error', ->
|
||||
@LabelsController.getAllLabels(@req, @res, @next)
|
||||
@next.callCount.should.equal 1
|
||||
expect(@next.lastCall.args[0]).to.be.instanceof Error
|
||||
|
||||
it 'should not send a json response', ->
|
||||
@LabelsController.getAllLabels(@req, @res, @next)
|
||||
@res.json.callCount.should.equal 0
|
||||
|
||||
describe 'broadcastLabelsForDoc', ->
|
||||
beforeEach ->
|
||||
@LabelsHandler.getLabelsForDoc = sinon.stub().callsArgWith(2, null, @fakeLabels)
|
||||
@EditorRealTimeController.emitToRoom = sinon.stub()
|
||||
@docId = 'somedoc'
|
||||
@req = {params: {project_id: @projectId, doc_id: @docId}}
|
||||
@res = {sendStatus: sinon.stub()}
|
||||
@next = sinon.stub()
|
||||
|
||||
it 'should call LabelsHandler.getLabelsForDoc', () ->
|
||||
@LabelsController.broadcastLabelsForDoc(@req, @res, @next)
|
||||
@LabelsHandler.getLabelsForDoc.callCount.should.equal 1
|
||||
@LabelsHandler.getLabelsForDoc.calledWith(@projectId).should.equal true
|
||||
|
||||
it 'should call not call next with an error', () ->
|
||||
@LabelsController.broadcastLabelsForDoc(@req, @res, @next)
|
||||
@next.callCount.should.equal 0
|
||||
|
||||
it 'should send a success response', () ->
|
||||
@LabelsController.broadcastLabelsForDoc(@req, @res, @next)
|
||||
@res.sendStatus.callCount.should.equal 1
|
||||
@res.sendStatus.calledWith(200).should.equal true
|
||||
|
||||
it 'should emit a message to room', () ->
|
||||
@LabelsController.broadcastLabelsForDoc(@req, @res, @next)
|
||||
@EditorRealTimeController.emitToRoom.callCount.should.equal 1
|
||||
lastCall = @EditorRealTimeController.emitToRoom.lastCall
|
||||
expect(lastCall.args[0]).to.equal @projectId
|
||||
expect(lastCall.args[1]).to.equal 'broadcastDocLabels'
|
||||
expect(lastCall.args[2]).to.have.all.keys ['docId', 'labels']
|
||||
|
||||
describe 'when LabelsHandler.getLabelsForDoc produces an error', ->
|
||||
beforeEach ->
|
||||
@LabelsHandler.getLabelsForDoc = sinon.stub().callsArgWith(2, new Error('woops'))
|
||||
@EditorRealTimeController.emitToRoom = sinon.stub()
|
||||
@docId = 'somedoc'
|
||||
@req = {params: {project_id: @projectId, doc_id: @docId}}
|
||||
@res = {json: sinon.stub()}
|
||||
@next = sinon.stub()
|
||||
|
||||
it 'should call LabelsHandler.getLabelsForDoc', () ->
|
||||
@LabelsController.broadcastLabelsForDoc(@req, @res, @next)
|
||||
@LabelsHandler.getLabelsForDoc.callCount.should.equal 1
|
||||
@LabelsHandler.getLabelsForDoc.calledWith(@projectId).should.equal true
|
||||
|
||||
it 'should call next with an error', ->
|
||||
@LabelsController.broadcastLabelsForDoc(@req, @res, @next)
|
||||
@next.callCount.should.equal 1
|
||||
expect(@next.lastCall.args[0]).to.be.instanceof Error
|
||||
|
||||
it 'should not send a json response', ->
|
||||
@LabelsController.broadcastLabelsForDoc(@req, @res, @next)
|
||||
@res.json.callCount.should.equal 0
|
|
@ -1,134 +0,0 @@
|
|||
chai = require('chai')
|
||||
chai.should()
|
||||
expect = chai.expect
|
||||
sinon = require("sinon")
|
||||
modulePath = "../../../../app/js/Features/Labels/LabelsHandler"
|
||||
SandboxedModule = require('sandboxed-module')
|
||||
|
||||
|
||||
describe 'LabelsHandler', ->
|
||||
beforeEach ->
|
||||
@projectId = 'someprojectid'
|
||||
@docId = 'somedocid'
|
||||
@ProjectEntityHandler = {
|
||||
getAllDocs: sinon.stub()
|
||||
getDoc: sinon.stub()
|
||||
}
|
||||
@DocumentUpdaterHandler = {
|
||||
flushDocToMongo: sinon.stub()
|
||||
}
|
||||
@LabelsHandler = SandboxedModule.require modulePath, requires:
|
||||
'../Project/ProjectEntityHandler': @ProjectEntityHandler
|
||||
'../DocumentUpdater/DocumentUpdaterHandler': @DocumentUpdaterHandler
|
||||
|
||||
describe 'extractLabelsFromDoc', ->
|
||||
beforeEach ->
|
||||
@lines = [
|
||||
'one',
|
||||
'two',
|
||||
'three \\label{aaa}',
|
||||
'four five',
|
||||
'\\label{bbb}',
|
||||
'six seven'
|
||||
]
|
||||
|
||||
it 'should extract all the labels', ->
|
||||
docLabels = @LabelsHandler.extractLabelsFromDoc @lines
|
||||
expect(docLabels).to.deep.equal ['aaa', 'bbb']
|
||||
|
||||
describe 'extractLabelsFromProjectDocs', ->
|
||||
beforeEach ->
|
||||
@docs = {
|
||||
'doc_one': {
|
||||
_id: 'id_one',
|
||||
lines: ['one', '\\label{aaa} two', 'three']
|
||||
},
|
||||
'doc_two': {
|
||||
_id: 'id_two',
|
||||
lines: ['four']
|
||||
},
|
||||
'doc_three': {
|
||||
_id: 'id_three',
|
||||
lines: ['\\label{bbb}', 'five six', 'seven eight \\label{ccc} nine']
|
||||
}
|
||||
}
|
||||
|
||||
it 'should extract all the labels', ->
|
||||
projectLabels = @LabelsHandler.extractLabelsFromProjectDocs @docs
|
||||
expect(projectLabels).to.deep.equal {
|
||||
'id_one': ['aaa'],
|
||||
'id_two': [],
|
||||
'id_three': ['bbb', 'ccc']
|
||||
}
|
||||
|
||||
describe 'getLabelsForDoc', ->
|
||||
beforeEach ->
|
||||
@fakeLines = ['one', '\\label{aaa}', 'two']
|
||||
@fakeLabels = ['aaa']
|
||||
@DocumentUpdaterHandler.flushDocToMongo = sinon.stub().callsArgWith(2, null)
|
||||
@ProjectEntityHandler.getDoc = sinon.stub().callsArgWith(2, null, @fakeLines)
|
||||
@LabelsHandler.extractLabelsFromDoc = sinon.stub().returns(@fakeLabels)
|
||||
@call = (callback) =>
|
||||
@LabelsHandler.getLabelsForDoc @projectId, @docId, callback
|
||||
|
||||
it 'should not produce an error', (done) ->
|
||||
@call (err, docLabels) =>
|
||||
expect(err).to.equal null
|
||||
done()
|
||||
|
||||
it 'should produce docLabels', (done) ->
|
||||
@call (err, docLabels) =>
|
||||
expect(docLabels).to.equal @fakeLabels
|
||||
done()
|
||||
|
||||
it 'should call flushDocToMongo', (done) ->
|
||||
@call (err, docLabels) =>
|
||||
@DocumentUpdaterHandler.flushDocToMongo.callCount.should.equal 1
|
||||
@DocumentUpdaterHandler.flushDocToMongo.calledWith(@projectId, @docId).should.equal true
|
||||
done()
|
||||
|
||||
it 'should call getDoc', (done) ->
|
||||
@call (err, docLabels) =>
|
||||
@ProjectEntityHandler.getDoc.callCount.should.equal 1
|
||||
@ProjectEntityHandler.getDoc.calledWith(@projectId, @docId).should.equal true
|
||||
done()
|
||||
|
||||
it 'should call extractLabelsFromDoc', (done) ->
|
||||
@call (err, docLabels) =>
|
||||
@LabelsHandler.extractLabelsFromDoc.callCount.should.equal 1
|
||||
@LabelsHandler.extractLabelsFromDoc.calledWith(@fakeLines).should.equal true
|
||||
done()
|
||||
|
||||
describe 'getAllLabelsForProject', ->
|
||||
beforeEach ->
|
||||
@fakeDocs = {
|
||||
'doc_one': {lines: ['\\label{aaa}']}
|
||||
}
|
||||
@fakeLabels = ['aaa']
|
||||
@DocumentUpdaterHandler.flushProjectToMongo = sinon.stub().callsArgWith(1, null)
|
||||
@ProjectEntityHandler.getAllDocs = sinon.stub().callsArgWith(1, null, @fakeDocs)
|
||||
@LabelsHandler.extractLabelsFromProjectDocs = sinon.stub().returns(@fakeLabels)
|
||||
@call = (callback) =>
|
||||
@LabelsHandler.getAllLabelsForProject @projectId, callback
|
||||
|
||||
it 'should not produce an error', (done) ->
|
||||
@call (err, projectLabels) =>
|
||||
expect(err).to.equal null
|
||||
done()
|
||||
|
||||
it 'should produce projectLabels', (done) ->
|
||||
@call (err, projectLabels) =>
|
||||
expect(projectLabels).to.equal @fakeLabels
|
||||
done()
|
||||
|
||||
it 'should call getAllDocs', (done) ->
|
||||
@call (err, projectLabels) =>
|
||||
@ProjectEntityHandler.getAllDocs.callCount.should.equal 1
|
||||
@ProjectEntityHandler.getAllDocs.calledWith(@projectId).should.equal true
|
||||
done()
|
||||
|
||||
it 'should call extractLabelsFromDoc', (done) ->
|
||||
@call (err, docLabels) =>
|
||||
@LabelsHandler.extractLabelsFromProjectDocs.callCount.should.equal 1
|
||||
@LabelsHandler.extractLabelsFromProjectDocs.calledWith(@fakeDocs).should.equal true
|
||||
done()
|
|
@ -0,0 +1,119 @@
|
|||
chai = require('chai')
|
||||
chai.should()
|
||||
expect = chai.expect
|
||||
sinon = require("sinon")
|
||||
modulePath = "../../../../app/js/Features/Metadata/MetaController"
|
||||
SandboxedModule = require('sandboxed-module')
|
||||
|
||||
|
||||
describe 'MetaController', ->
|
||||
beforeEach ->
|
||||
@projectId = 'somekindofid'
|
||||
@EditorRealTimeController = {
|
||||
emitToRoom: sinon.stub()
|
||||
}
|
||||
@MetaHandler = {
|
||||
getAllMetaForProject: sinon.stub()
|
||||
getMetaForDoc: sinon.stub()
|
||||
}
|
||||
@MetadataController = SandboxedModule.require modulePath, requires:
|
||||
'logger-sharelatex': {log: sinon.stub(), err: sinon.stub()}
|
||||
'../Editor/EditorRealTimeController': @EditorRealTimeController
|
||||
'./MetaHandler': @MetaHandler
|
||||
|
||||
describe 'getMetadata', ->
|
||||
beforeEach ->
|
||||
@fakeLabels = {'somedoc': ['a_label']}
|
||||
@MetaHandler.getAllMetaForProject = sinon.stub().callsArgWith(1, null, @fakeLabels)
|
||||
@req = {params: {project_id: @projectId}}
|
||||
@res = {json: sinon.stub()}
|
||||
@next = sinon.stub()
|
||||
|
||||
it 'should call MetaHandler.getAllMetaForProject', () ->
|
||||
@MetadataController.getMetadata(@req, @res, @next)
|
||||
@MetaHandler.getAllMetaForProject.callCount.should.equal 1
|
||||
@MetaHandler.getAllMetaForProject.calledWith(@projectId).should.equal true
|
||||
|
||||
it 'should call not call next with an error', () ->
|
||||
@MetadataController.getMetadata(@req, @res, @next)
|
||||
@next.callCount.should.equal 0
|
||||
|
||||
it 'should send a json response', () ->
|
||||
@MetadataController.getMetadata(@req, @res, @next)
|
||||
@res.json.callCount.should.equal 1
|
||||
expect(@res.json.lastCall.args[0]).to.have.all.keys ['projectId', 'projectMeta']
|
||||
|
||||
describe 'when MetaHandler.getAllMetaForProject produces an error', ->
|
||||
beforeEach ->
|
||||
@MetaHandler.getAllMetaForProject = sinon.stub().callsArgWith(1, new Error('woops'))
|
||||
@req = {params: {project_id: @projectId}}
|
||||
@res = {json: sinon.stub()}
|
||||
@next = sinon.stub()
|
||||
|
||||
it 'should call MetaHandler.getAllMetaForProject', () ->
|
||||
@MetadataController.getMetadata(@req, @res, @next)
|
||||
@MetaHandler.getAllMetaForProject.callCount.should.equal 1
|
||||
@MetaHandler.getAllMetaForProject.calledWith(@projectId).should.equal true
|
||||
|
||||
it 'should call next with an error', ->
|
||||
@MetadataController.getMetadata(@req, @res, @next)
|
||||
@next.callCount.should.equal 1
|
||||
expect(@next.lastCall.args[0]).to.be.instanceof Error
|
||||
|
||||
it 'should not send a json response', ->
|
||||
@MetadataController.getMetadata(@req, @res, @next)
|
||||
@res.json.callCount.should.equal 0
|
||||
|
||||
describe 'broadcastMetadataForDoc', ->
|
||||
beforeEach ->
|
||||
@MetaHandler.getMetaForDoc = sinon.stub().callsArgWith(2, null, @fakeLabels)
|
||||
@EditorRealTimeController.emitToRoom = sinon.stub()
|
||||
@docId = 'somedoc'
|
||||
@req = {params: {project_id: @projectId, doc_id: @docId}}
|
||||
@res = {sendStatus: sinon.stub()}
|
||||
@next = sinon.stub()
|
||||
|
||||
it 'should call MetaHandler.getMetaForDoc', () ->
|
||||
@MetadataController.broadcastMetadataForDoc(@req, @res, @next)
|
||||
@MetaHandler.getMetaForDoc.callCount.should.equal 1
|
||||
@MetaHandler.getMetaForDoc.calledWith(@projectId).should.equal true
|
||||
|
||||
it 'should call not call next with an error', () ->
|
||||
@MetadataController.broadcastMetadataForDoc(@req, @res, @next)
|
||||
@next.callCount.should.equal 0
|
||||
|
||||
it 'should send a success response', () ->
|
||||
@MetadataController.broadcastMetadataForDoc(@req, @res, @next)
|
||||
@res.sendStatus.callCount.should.equal 1
|
||||
@res.sendStatus.calledWith(200).should.equal true
|
||||
|
||||
it 'should emit a message to room', () ->
|
||||
@MetadataController.broadcastMetadataForDoc(@req, @res, @next)
|
||||
@EditorRealTimeController.emitToRoom.callCount.should.equal 1
|
||||
lastCall = @EditorRealTimeController.emitToRoom.lastCall
|
||||
expect(lastCall.args[0]).to.equal @projectId
|
||||
expect(lastCall.args[1]).to.equal 'broadcastDocMeta'
|
||||
expect(lastCall.args[2]).to.have.all.keys ['docId', 'meta']
|
||||
|
||||
describe 'when MetaHandler.getMetaForDoc produces an error', ->
|
||||
beforeEach ->
|
||||
@MetaHandler.getMetaForDoc = sinon.stub().callsArgWith(2, new Error('woops'))
|
||||
@EditorRealTimeController.emitToRoom = sinon.stub()
|
||||
@docId = 'somedoc'
|
||||
@req = {params: {project_id: @projectId, doc_id: @docId}}
|
||||
@res = {json: sinon.stub()}
|
||||
@next = sinon.stub()
|
||||
|
||||
it 'should call MetaHandler.getMetaForDoc', () ->
|
||||
@MetadataController.broadcastMetadataForDoc(@req, @res, @next)
|
||||
@MetaHandler.getMetaForDoc.callCount.should.equal 1
|
||||
@MetaHandler.getMetaForDoc.calledWith(@projectId).should.equal true
|
||||
|
||||
it 'should call next with an error', ->
|
||||
@MetadataController.broadcastMetadataForDoc(@req, @res, @next)
|
||||
@next.callCount.should.equal 1
|
||||
expect(@next.lastCall.args[0]).to.be.instanceof Error
|
||||
|
||||
it 'should not send a json response', ->
|
||||
@MetadataController.broadcastMetadataForDoc(@req, @res, @next)
|
||||
@res.json.callCount.should.equal 0
|
243
services/web/test/unit/coffee/Metadata/MetaHandlerTests.coffee
Normal file
243
services/web/test/unit/coffee/Metadata/MetaHandlerTests.coffee
Normal file
|
@ -0,0 +1,243 @@
|
|||
chai = require('chai')
|
||||
chai.should()
|
||||
expect = chai.expect
|
||||
sinon = require("sinon")
|
||||
modulePath = "../../../../app/js/Features/Metadata/MetaHandler"
|
||||
SandboxedModule = require('sandboxed-module')
|
||||
|
||||
|
||||
describe 'MetaHandler', ->
|
||||
beforeEach ->
|
||||
@projectId = 'someprojectid'
|
||||
@docId = 'somedocid'
|
||||
@ProjectEntityHandler = {
|
||||
getAllDocs: sinon.stub()
|
||||
getDoc: sinon.stub()
|
||||
}
|
||||
@DocumentUpdaterHandler = {
|
||||
flushDocToMongo: sinon.stub()
|
||||
}
|
||||
@packageMapping =
|
||||
foo: [
|
||||
{
|
||||
caption: '\\bar'
|
||||
snippet: '\\bar'
|
||||
meta: 'foo-cmd'
|
||||
score: 12
|
||||
}, {
|
||||
caption: '\\bat[]{}'
|
||||
snippet: '\\bar[$1]{$2}'
|
||||
meta: 'foo-cmd'
|
||||
score: 10
|
||||
}
|
||||
],
|
||||
baz: [
|
||||
{
|
||||
caption: '\\longercommandtest{}'
|
||||
snippet: '\\longercommandtest{$1}'
|
||||
meta: 'baz-cmd'
|
||||
score: 50
|
||||
}
|
||||
]
|
||||
|
||||
@MetaHandler = SandboxedModule.require modulePath, requires:
|
||||
'../Project/ProjectEntityHandler': @ProjectEntityHandler
|
||||
'../DocumentUpdater/DocumentUpdaterHandler': @DocumentUpdaterHandler
|
||||
'./packageMapping': @packageMapping
|
||||
|
||||
describe 'extractMetaFromDoc', ->
|
||||
beforeEach ->
|
||||
@lines = [
|
||||
'\\usepackage{foo}'
|
||||
'\\usepackage{amsmath, booktabs}'
|
||||
'one'
|
||||
'two'
|
||||
'three \\label{aaa}'
|
||||
'four five'
|
||||
'\\label{bbb}'
|
||||
'six seven'
|
||||
]
|
||||
|
||||
it 'should extract all the labels and packages', ->
|
||||
docMeta = @MetaHandler.extractMetaFromDoc @lines
|
||||
expect(docMeta).to.deep.equal {
|
||||
labels: ['aaa', 'bbb']
|
||||
packages:
|
||||
foo: [
|
||||
{
|
||||
caption: '\\bar'
|
||||
snippet: '\\bar'
|
||||
meta: 'foo-cmd'
|
||||
score: 12
|
||||
}, {
|
||||
caption: '\\bat[]{}'
|
||||
snippet: '\\bar[$1]{$2}'
|
||||
meta: 'foo-cmd'
|
||||
score: 10
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
describe 'extractMetaFromProjectDocs', ->
|
||||
beforeEach ->
|
||||
@docs =
|
||||
'doc_one':
|
||||
_id: 'id_one'
|
||||
lines: ['one', '\\label{aaa} two', 'three']
|
||||
'doc_two':
|
||||
_id: 'id_two'
|
||||
lines: ['four']
|
||||
'doc_three':
|
||||
_id: 'id_three'
|
||||
lines: [
|
||||
'\\label{bbb}'
|
||||
'five six'
|
||||
'seven eight \\label{ccc} nine'
|
||||
]
|
||||
'doc_four':
|
||||
_id: 'id_four'
|
||||
lines: [
|
||||
'\\usepackage[width=\\textwidth]{baz}'
|
||||
'\\usepackage{amsmath}'
|
||||
]
|
||||
'doc_five':
|
||||
_id: 'id_five'
|
||||
lines: [
|
||||
'\\usepackage{foo,baz}'
|
||||
'\\usepackage[options=foo]{hello}'
|
||||
'some text'
|
||||
'\\section{this}\\label{sec:intro}'
|
||||
'In Section \\ref{sec:intro} we saw'
|
||||
'nothing'
|
||||
]
|
||||
|
||||
it 'should extract all metadata', ->
|
||||
projectMeta = @MetaHandler.extractMetaFromProjectDocs @docs
|
||||
expect(projectMeta).to.deep.equal {
|
||||
'id_one': {labels: ['aaa'], packages: {}}
|
||||
'id_two': {labels: [], packages: {}}
|
||||
'id_three': {labels: ['bbb', 'ccc'], packages: {}}
|
||||
'id_four':
|
||||
labels: []
|
||||
packages:
|
||||
baz: [{
|
||||
caption: '\\longercommandtest{}'
|
||||
snippet: '\\longercommandtest{$1}'
|
||||
meta: 'baz-cmd'
|
||||
score: 50}]
|
||||
'id_five':
|
||||
labels: ['sec:intro']
|
||||
packages:
|
||||
foo: [
|
||||
{
|
||||
caption: '\\bar'
|
||||
snippet: '\\bar'
|
||||
meta: 'foo-cmd'
|
||||
score: 12
|
||||
}, {
|
||||
caption: '\\bat[]{}'
|
||||
snippet: '\\bar[$1]{$2}'
|
||||
meta: 'foo-cmd'
|
||||
score: 10
|
||||
}
|
||||
]
|
||||
baz: [
|
||||
{
|
||||
caption: '\\longercommandtest{}'
|
||||
snippet: '\\longercommandtest{$1}'
|
||||
meta: 'baz-cmd'
|
||||
score: 50
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
describe 'getMetaForDoc', ->
|
||||
beforeEach ->
|
||||
@fakeLines = ['\\usepackage{abc}', 'one', '\\label{aaa}', 'two']
|
||||
@fakeMeta = {labels: ['aaa'], packages: ['abc']}
|
||||
@DocumentUpdaterHandler.flushDocToMongo = sinon.stub().callsArgWith 2, null
|
||||
@ProjectEntityHandler.getDoc = sinon.stub().callsArgWith 2, null, @fakeLines
|
||||
@MetaHandler.extractMetaFromDoc = sinon.stub().returns @fakeMeta
|
||||
@call = (callback) =>
|
||||
@MetaHandler.getMetaForDoc @projectId, @docId, callback
|
||||
|
||||
it 'should not produce an error', (done) ->
|
||||
@call (err, docMeta) =>
|
||||
expect(err).to.equal null
|
||||
done()
|
||||
|
||||
it 'should produce docMeta', (done) ->
|
||||
@call (err, docMeta) =>
|
||||
expect(docMeta).to.equal @fakeMeta
|
||||
done()
|
||||
|
||||
it 'should call flushDocToMongo', (done) ->
|
||||
@call (err, docMeta) =>
|
||||
@DocumentUpdaterHandler.flushDocToMongo.callCount.should.equal 1
|
||||
@DocumentUpdaterHandler.flushDocToMongo.calledWith(@projectId, @docId).should.equal true
|
||||
done()
|
||||
|
||||
it 'should call getDoc', (done) ->
|
||||
@call (err, docMeta) =>
|
||||
@ProjectEntityHandler.getDoc.callCount.should.equal 1
|
||||
@ProjectEntityHandler.getDoc.calledWith(@projectId, @docId).should.equal true
|
||||
done()
|
||||
|
||||
it 'should call extractMetaFromDoc', (done) ->
|
||||
@call (err, docMeta) =>
|
||||
@MetaHandler.extractMetaFromDoc.callCount.should.equal 1
|
||||
@MetaHandler.extractMetaFromDoc.calledWith(@fakeLines).should.equal true
|
||||
done()
|
||||
|
||||
describe 'getAllMetaForProject', ->
|
||||
beforeEach ->
|
||||
@fakeDocs =
|
||||
'doc_one':
|
||||
lines: [
|
||||
'\\usepackage[some-options,more=foo]{foo}'
|
||||
'\\label{aaa}'
|
||||
]
|
||||
|
||||
@fakeMeta =
|
||||
labels: ['aaa']
|
||||
packages:
|
||||
foo: [
|
||||
{
|
||||
caption: '\\bar'
|
||||
snippet: '\\bar'
|
||||
meta: 'foo-cmd'
|
||||
score: 12
|
||||
}, {
|
||||
caption: '\\bat[]{}'
|
||||
snippet: '\\bar[$1]{$2}'
|
||||
meta: 'foo-cmd'
|
||||
score: 10
|
||||
}
|
||||
]
|
||||
@DocumentUpdaterHandler.flushProjectToMongo = sinon.stub().callsArgWith 1, null
|
||||
@ProjectEntityHandler.getAllDocs = sinon.stub().callsArgWith 1, null, @fakeDocs
|
||||
@MetaHandler.extractMetaFromProjectDocs = sinon.stub().returns @fakeMeta
|
||||
@call = (callback) =>
|
||||
@MetaHandler.getAllMetaForProject @projectId, callback
|
||||
|
||||
it 'should not produce an error', (done) ->
|
||||
@call (err, projectMeta) =>
|
||||
expect(err).to.equal null
|
||||
done()
|
||||
|
||||
it 'should produce projectMeta', (done) ->
|
||||
@call (err, projectMeta) =>
|
||||
expect(projectMeta).to.equal @fakeMeta
|
||||
done()
|
||||
|
||||
it 'should call getAllDocs', (done) ->
|
||||
@call (err, projectMeta) =>
|
||||
@ProjectEntityHandler.getAllDocs.callCount.should.equal 1
|
||||
@ProjectEntityHandler.getAllDocs.calledWith(@projectId).should.equal true
|
||||
done()
|
||||
|
||||
it 'should call extractMetaFromDoc', (done) ->
|
||||
@call (err, docMeta) =>
|
||||
@MetaHandler.extractMetaFromProjectDocs.callCount.should.equal 1
|
||||
@MetaHandler.extractMetaFromProjectDocs.calledWith(@fakeDocs).should.equal true
|
||||
done()
|
Loading…
Reference in a new issue