Просмотр исходного кода

Merge pull request #290 from sharelatex/enable-chktex

Enable chktex
Brian Gough 10 лет назад
Родитель
Сommit
abcfb2dd16

+ 1 - 0
services/web/app/coffee/Features/Compile/ClsiManager.coffee

@@ -147,6 +147,7 @@ module.exports = ClsiManager =
 								timeout: options.timeout
 								imageName: project.imageName
 								draft: !!options.draft
+								check: options.check
 							rootResourcePath: rootResourcePath
 							resources: resources
 					}

+ 2 - 0
services/web/app/coffee/Features/Compile/CompileController.coffee

@@ -29,6 +29,8 @@ module.exports = CompileController =
 				options.compiler = req.body.compiler
 			if req.body?.draft
 				options.draft = req.body.draft
+			if req.body?.check
+				options.check = if req.body.check is "validate" then "validate" else undefined
 			logger.log {options:options, project_id:project_id, user_id:user_id}, "got compile request"
 			CompileManager.compile project_id, user_id, options, (error, status, outputFiles, clsiServerId, limits, validationProblems) ->
 				return next(error) if error?

+ 11 - 3
services/web/app/views/project/editor/pdf.jade

@@ -25,7 +25,7 @@ div.full-size.pdf(ng-controller="PdfController")
 				dropdown-toggle
 			)
 				span.caret
-			ul.dropdown-menu.dropdown-menu-right
+			ul.dropdown-menu.dropdown-menu-left
 				li.dropdown-header #{translate("compile_mode")}
 				li
 					a(href, ng-click="draft = false")
@@ -36,6 +36,13 @@ div.full-size.pdf(ng-controller="PdfController")
 						i.fa.fa-fw(ng-class="{'fa-check': draft}")
 						|  #{translate("fast")} 
 						span.subdued [draft]
+				if user.betaProgram
+					li.dropdown-header #{translate("file_checks")}
+					li
+						a(href, ng-click="recompile({check:true})")
+							i.fa.fa-fw()
+							|  #{translate("run_syntax_check")}
+							span.beta-feature-badge
 		a(
 			href
 			ng-click="stop()"
@@ -115,7 +122,8 @@ div.full-size.pdf(ng-controller="PdfController")
 					|  
 					span(ng-show="entry.file") {{ entry.file }}
 					span(ng-show="entry.line") , line {{ entry.line }}
-				p.entry-message(ng-show="entry.message") {{ entry.message }}
+				p.entry-message(ng-show="entry.message")
+					| {{ entry.type }} {{ entry.message }}
 				.card.card-hint(
 					ng-if="entry.humanReadableHint"
 					stop-propagation="click"
@@ -126,7 +134,7 @@ div.full-size.pdf(ng-controller="PdfController")
 						ng-show="entry.humanReadableHint",
 						ng-bind-html="wikiEnabled ? entry.humanReadableHint : stripHTMLFromString(entry.humanReadableHint)")
 					.card-hint-actions.clearfix
-						.card-hint-ext-link(ng-if="wikiEnabled")
+						.card-hint-ext-link(ng-if="wikiEnabled && entry.extraInfoURL")
 							a(
 								ng-href="{{ entry.extraInfoURL }}",
 								ng-click="trackLogHintsLearnMore()"

+ 1 - 1
services/web/public/coffee/ide/editor/EditorManager.coffee

@@ -46,7 +46,7 @@ define [
 
 			done = () =>
 				if options.gotoLine?
-					@$scope.$broadcast "editor:gotoLine", options.gotoLine
+					@$scope.$broadcast "editor:gotoLine", options.gotoLine, options.gotoColumn
 			
 			if doc.id == @$scope.editor.open_doc_id and !options.forceReopen
 				@$scope.$apply () =>

+ 6 - 6
services/web/public/coffee/ide/editor/directives/aceEditor/cursor-position/CursorPositionManager.coffee

@@ -23,10 +23,10 @@ define [], () ->
 				@storeCursorPosition(@editor.getSession())
 				@storeScrollTopPosition(@editor.getSession())
 
-			@$scope.$on "#{@$scope.name}:gotoLine", (editor, value) =>
-				if value?
+			@$scope.$on "#{@$scope.name}:gotoLine", (editor, line, column) =>
+				if line?
 					setTimeout () =>
-						@gotoLine(value)
+						@gotoLine(line, column)
 					, 10 # Hack: Must happen after @gotoStoredPosition
 
 		storeScrollTopPosition: (session) ->
@@ -53,6 +53,6 @@ define [], () ->
 			@editor.getSession().setScrollTop(pos.scrollTop or 0)
 			delete @ignoreCursorPositionChanges
 
-		gotoLine: (line) ->
-			@editor.gotoLine(line)
-			@editor.focus()
+		gotoLine: (line, column) ->
+			@editor.gotoLine(line, column)
+			@editor.focus()

+ 10 - 2
services/web/public/coffee/ide/human-readable-logs/HumanReadableLogs.coffee

@@ -3,7 +3,10 @@ define [
 	"ide/human-readable-logs/HumanReadableLogsRules"
 ], (LogParser, ruleset) ->
 	parse : (rawLog, options) ->
-		parsedLogEntries = LogParser.parse(rawLog, options)
+		if typeof rawLog is 'string'
+			parsedLogEntries = LogParser.parse(rawLog, options)
+		else
+			parsedLogEntries = rawLog
 
 		_getRule = (logMessage) ->
 			return rule for rule in ruleset when rule.regexToMatch.test logMessage
@@ -12,7 +15,12 @@ define [
 			ruleDetails = _getRule entry.message
 
 			if (ruleDetails?)
-				entry.ruleId = 'hint_' + ruleDetails.regexToMatch.toString().replace(/\s/g, '_').slice(1, -1) if ruleDetails.regexToMatch?
+				if ruleDetails.ruleId?
+					entry.ruleId = ruleDetails.ruleId
+				else if ruleDetails.regexToMatch?
+					entry.ruleId = 'hint_' + ruleDetails.regexToMatch.toString().replace(/\s/g, '_').slice(1, -1)
+				if ruleDetails.newMessage?
+					entry.message = entry.message.replace ruleDetails.regexToMatch, ruleDetails.newMessage
 				
 				entry.humanReadableHint = ruleDetails.humanReadableHint if ruleDetails.humanReadableHint?
 				entry.extraInfoURL = ruleDetails.extraInfoURL if ruleDetails.extraInfoURL?

+ 15 - 0
services/web/public/coffee/ide/human-readable-logs/HumanReadableLogsRules.coffee

@@ -88,4 +88,19 @@ define -> [
 		humanReadableHint: """
 			You have used a font command which is only available in math mode. To use this command, you must be in maths mode (E.g. $ \u2026 $ or \\begin{math} \u2026 \\end{math}). If you want to use it outside of math mode, use the text version instead: \\textrm, \\textit, etc.
 		"""
+	,
+		ruleId: "hint_mismatched_environment"
+		regexToMatch: /Error: `([^']{2,})' expected, found `([^']{2,})'.*/
+		newMessage: "Error: environment does not match \\begin{$1} ... \\end{$2}"
+		humanReadableHint: """
+			You have used \\begin{...} without a corresponding \\end{...}.
+		"""
+	,
+		ruleId: "hint_mismatched_brackets"
+		regexToMatch: /Error: `([^a-zA-Z0-9])' expected, found `([^a-zA-Z0-9])'.*/
+		newMessage: "Error: brackets do not match, found '$2' instead of '$1'"
+		humanReadableHint: """
+			You have used an open bracket without a corresponding close bracket.
+		"""
+
 ]

+ 76 - 20
services/web/public/coffee/ide/pdf/controllers/PdfController.coffee

@@ -86,11 +86,15 @@ define [
 			return $http.post url, {
 				rootDoc_id: options.rootDocOverride_id or null
 				draft: $scope.draft
+				check: if options.check then "validate" else null
 				_csrf: window.csrfToken
 			}, {params: params}
 
 		parseCompileResponse = (response) ->		
 
+			# keep last url
+			last_pdf_url = $scope.pdf.url
+
 			# Reset everything
 			$scope.pdf.error      = false
 			$scope.pdf.timedout   = false
@@ -120,11 +124,23 @@ define [
 			if response.status == "timedout"
 				$scope.pdf.view = 'errors'
 				$scope.pdf.timedout = true
-				fetchLogs(fileByPath['output.log'], fileByPath['output.blg'])
+				fetchLogs(fileByPath)
 			else if response.status == "terminated"
 				$scope.pdf.view = 'errors'
 				$scope.pdf.compileTerminated = true
-				fetchLogs(fileByPath['output.log'], fileByPath['output.blg'])
+				fetchLogs(fileByPath)
+			else if response.status in ["validation-fail", "validation-pass"]
+				$scope.pdf.view = 'pdf'
+				$scope.pdf.compileExited = true
+				$scope.pdf.url = last_pdf_url
+				$scope.shouldShowLogs = true
+				fetchLogs(fileByPath, { validation: true })
+			else if response.status == "exited"
+				$scope.pdf.view = 'pdf'
+				$scope.pdf.compileExited = true
+				$scope.pdf.url = last_pdf_url
+				$scope.shouldShowLogs = true
+				fetchLogs(fileByPath)
 			else if response.status == "autocompile-backoff"
 				$scope.pdf.view = 'uncompiled'
 			else if response.status == "project-too-large"
@@ -134,7 +150,7 @@ define [
 				$scope.pdf.view = 'errors'
 				$scope.pdf.failure = true
 				$scope.shouldShowLogs = true
-				fetchLogs(fileByPath['output.log'], fileByPath['output.blg'])
+				fetchLogs(fileByPath)
 			else if response.status == 'clsi-maintenance'
 				$scope.pdf.view = 'errors'
 				$scope.pdf.clsiMaintenance = true
@@ -165,7 +181,7 @@ define [
 				qs.popupDownload = true
 				$scope.pdf.downloadUrl = "/project/#{$scope.project_id}/output/output.pdf" + createQueryString(qs)
 
-				fetchLogs(fileByPath['output.log'], fileByPath['output.blg'])
+				fetchLogs(fileByPath)
 
 			IGNORE_FILES = ["output.fls", "output.fdb_latexmk"]
 			$scope.pdf.outputFiles = []
@@ -186,7 +202,13 @@ define [
 					}
 
 
-		fetchLogs = (logFile, blgFile) ->
+		fetchLogs = (fileByPath, options) ->
+
+			if options?.validation
+				chktexFile = fileByPath['output.chktex']
+			else
+				logFile = fileByPath['output.log']
+				blgFile = fileByPath['output.blg']
 
 			getFile = (name, file) ->
 				opts =
@@ -213,6 +235,8 @@ define [
 
 			accumulateResults = (newEntries) ->
 				for key in ['all', 'errors', 'warnings']
+					if newEntries.type?
+						entry.type = newEntries.type for entry in newEntries[key]
 					logEntries[key] = logEntries[key].concat newEntries[key]
 
 			# use the parsers for each file type
@@ -222,10 +246,24 @@ define [
 				all = [].concat errors, warnings, typesetting
 				accumulateResults {all, errors, warnings}
 
+			processChkTex = (log) ->
+				errors = []
+				warnings = []
+				for line in log.split("\n")
+					if m = line.match /^(\S+):(\d+):(\d+): (Error|Warning): (.*)/
+						result = { file:m[1], line:m[2], column:m[3], level:m[4].toLowerCase(), message: "#{m[4]}: #{m[5]}"}
+						if result.level is 'error'
+							errors.push result
+						else
+							warnings.push result
+				all = [].concat errors, warnings
+				logHints = HumanReadableLogs.parse {type: "Validation", all, errors, warnings}
+				accumulateResults logHints
+
 			processBiber = (log) ->
 				{errors, warnings} = BibLogParser.parse(log, {})
 				all = [].concat errors, warnings
-				accumulateResults {all, errors, warnings}
+				accumulateResults {type: "BibTeX", all, errors, warnings}
 
 			# output the results
 			handleError = () ->
@@ -248,19 +286,35 @@ define [
 							}
 
 			# retrieve the logfile and process it
-			response = getFile('output.log', logFile)
-				.success processLog
-				.error handleError
-
-			if blgFile?	# retrieve the blg file if present
-				response.success () ->
-					getFile('output.blg', blgFile)
-						# ignore errors in biber file
-						.success processBiber
-						# display the combined result
-						.then annotateFiles
-			else # otherwise just display the result
-				response.success annotateFiles
+			if logFile?
+				response = getFile('output.log', logFile)
+					.then	(response) -> processLog(response.data)
+
+				if blgFile?	# retrieve the blg file if present
+					response = response.then () ->
+						getFile('output.blg', blgFile)
+							.then(
+								(response) -> processBiber(response.data),
+								() ->	true # ignore errors in biber file
+							)
+
+			if response?
+				response.catch handleError
+			else
+				handleError()
+
+			if chktexFile?
+				getChkTex = () ->
+					getFile('output.chktex', chktexFile)
+						.then	(response) -> processChkTex(response.data)
+				# always retrieve the chktex file if present
+				if response?
+					response = response.then getChkTex, getChkTex
+				else
+					response = getChkTex()
+
+			# display the combined result
+			response.finally annotateFiles
 
 		getRootDocOverride_id = () ->
 			doc = ide.editorManager.getCurrentDocValue()
@@ -504,7 +558,9 @@ define [
 			return if !entity? or entity.type != "doc"
 			if entry.line?
 				line = entry.line
-			ide.editorManager.openDoc(entity, gotoLine: line)
+			if entry.column?
+				column = entry.column
+			ide.editorManager.openDoc(entity, gotoLine: line, gotoColumn: column)
 	]
 
 	App.controller 'ClearCacheModalController', ["$scope", "$modalInstance", ($scope, $modalInstance) ->

+ 1 - 0
services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee

@@ -174,6 +174,7 @@ describe "ClsiManager", ->
 							timeout : 100
 							imageName: @image
 							draft: false
+							check: undefined
 						rootResourcePath: "main.tex"
 						resources: [{
 							path:    "main.tex"