Merge pull request #10111 from overleaf/mj-lezer-file-outline

[web] Parser backed file outline

GitOrigin-RevId: 0825f4887ba4dae24a14dd1880d07b791d0b4cd9
This commit is contained in:
Mathias Jakobsen 2022-11-03 12:43:59 +00:00 committed by Copybot
parent 986d52b8d4
commit a54b633726
10 changed files with 82 additions and 4 deletions

View file

@ -34,4 +34,5 @@ aside.editor-sidebar.full-size(
on-toggle="onToggle"
event-tracking="eventTracking"
highlighted-line="highlightedLine"
show="show"
)

View file

@ -1,4 +1,5 @@
window.i18n = { currentLangCode: 'en' }
window.ExposedSettings = {
appName: 'Overleaf',
validRootDocExtensions: ['tex', 'Rtex', 'ltx'],
} as typeof window.ExposedSettings

View file

@ -107,6 +107,9 @@ OutlineItem.propTypes = {
title: PropTypes.string.isRequired,
level: PropTypes.number,
children: PropTypes.array,
// Used for caching in CM6
from: PropTypes.number,
to: PropTypes.number,
}).isRequired,
jumpToLine: PropTypes.func.isRequired,
highlightedLine: PropTypes.number,

View file

@ -16,6 +16,7 @@ const OutlinePane = React.memo(function OutlinePane({
onToggle,
eventTracking,
highlightedLine,
show,
}) {
const { t } = useTranslation()
@ -46,6 +47,12 @@ const OutlinePane = React.memo(function OutlinePane({
}
}
// NOTE: This flag is for disabling the rendering of the component. Used while
// both an Angular and React-based file outline is present in the code base.
if (!show) {
return null
}
return (
<div className={headerClasses}>
<header className="outline-header">
@ -82,6 +89,7 @@ OutlinePane.propTypes = {
onToggle: PropTypes.func.isRequired,
eventTracking: PropTypes.object.isRequired,
highlightedLine: PropTypes.number,
show: PropTypes.bool.isRequired,
}
export default withErrorBoundary(OutlinePane)

View file

@ -8,6 +8,23 @@ App.controller('OutlineController', function ($scope, ide, eventTracking) {
$scope.outline = []
$scope.eventTracking = eventTracking
function shouldShowOutline() {
if ($scope.editor.showRichText) {
return true
}
return !$scope.editor.newSourceEditor
}
$scope.show = shouldShowOutline()
$scope.$watch('editor.newSourceEditor', function () {
$scope.show = shouldShowOutline()
})
$scope.$watch('editor.showRichText', function () {
$scope.show = shouldShowOutline()
})
$scope.$on('outline-manager:outline-changed', onOutlineChange)
function onOutlineChange(e, outlineInfo) {
@ -40,5 +57,6 @@ App.component(
'eventTracking',
'onToggle',
'isTexFile',
'show',
])
)

View file

@ -16,6 +16,13 @@ class OutlineManager {
this.ignoreNextScroll = false
this.ignoreNextCursorUpdate = false
scope.$watch('editor.newSourceEditor', (now, before) => {
if (before && !now) {
this.updateOutline()
this.broadcastChangeEvent()
}
})
scope.$on('doc:after-opened', (ev, { isNewDoc }) => {
if (isNewDoc) {
// if a new doc is opened a cursor updates will be triggered before the
@ -65,10 +72,25 @@ class OutlineManager {
scope.$watch('editor.showRichText', () => {
this.ignoreNextScroll = true
this.ignoreNextCursorUpdate = true
if (this.shouldShowOutline()) {
this.updateOutline()
this.broadcastChangeEvent()
}
})
}
shouldShowOutline() {
if (this.scope.editor.showRichText) {
return true
}
return !this.scope.editor.newSourceEditor
}
updateOutline() {
// Disable if using CM6
if (!this.shouldShowOutline()) {
return
}
if (this.isTexFile) {
const content = this.ide.editorManager.getCurrentDocValue()
if (content) {
@ -103,6 +125,10 @@ class OutlineManager {
}
broadcastChangeEvent() {
// Disable if using CM6
if (!this.shouldShowOutline()) {
return
}
this.scope.$broadcast('outline-manager:outline-changed', {
isTexFile: this.isTexFile,
outline: this.outline,

View file

@ -231,7 +231,7 @@ export default EditorManager = (function () {
const done = isNewDoc => {
const eventName = 'doc:after-opened'
this.$scope.$broadcast(eventName, { isNewDoc })
window.dispatchEvent(new CustomEvent(eventName, { isNewDoc }))
window.dispatchEvent(new CustomEvent(eventName, { detail: isNewDoc }))
if (options.gotoLine != null) {
// allow Ace to display document before moving, delay until next tick
// added delay to make this happen later that gotoStoredPosition in

View file

@ -0,0 +1,18 @@
import { DependencyList, useMemo, useRef } from 'react'
import { isEqual } from 'lodash'
function useDeepCompare(dependencies: DependencyList) {
const ref = useRef<DependencyList>([])
if (!isEqual(ref.current, dependencies)) {
ref.current = dependencies
}
return ref.current
}
export default function useDeepCompareMemo<T>(
factory: () => T,
dependencies: DependencyList
) {
// eslint-disable-next-line react-hooks/exhaustive-deps
return useMemo(factory, useDeepCompare(dependencies))
}

View file

@ -10,11 +10,11 @@ export default function useScopeEventEmitter(eventName, broadcast = true) {
const { $scope } = useIdeContext()
return useCallback(
detail => {
(...detail) => {
if (broadcast) {
$scope.$broadcast(eventName, detail)
$scope.$broadcast(eventName, ...detail)
} else {
$scope.$emit(eventName, detail)
$scope.$emit(eventName, ...detail)
}
},
[$scope, eventName, broadcast]

View file

@ -55,6 +55,7 @@ describe('<OutlinePane />', function () {
jumpToLine={jumpToLine}
onToggle={onToggle}
eventTracking={eventTracking}
show
/>
)
@ -70,6 +71,7 @@ describe('<OutlinePane />', function () {
jumpToLine={jumpToLine}
onToggle={onToggle}
eventTracking={eventTracking}
show
/>
)
@ -98,6 +100,7 @@ describe('<OutlinePane />', function () {
jumpToLine={jumpToLine}
onToggle={onToggle}
eventTracking={eventTracking}
show
/>
)