outline-manager.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import { isEqual, cloneDeep } from 'lodash'
  2. import './controllers/outline-controller'
  3. import { matchOutline, nestOutline } from './outline-parser'
  4. import isValidTeXFile from '../../main/is-valid-tex-file'
  5. class OutlineManager {
  6. constructor(ide, scope) {
  7. this.ide = ide
  8. this.scope = scope
  9. this.shareJsDoc = null
  10. this.isTexFile = false
  11. this.flatOutline = []
  12. this.previousFlatOutline = []
  13. this.outline = []
  14. this.highlightedLine = null
  15. this.ignoreNextScroll = false
  16. this.ignoreNextCursorUpdate = false
  17. scope.$watch('editor.newSourceEditor', (now, before) => {
  18. if (before && !now) {
  19. this.updateOutline()
  20. this.broadcastChangeEvent()
  21. }
  22. })
  23. scope.$on('doc:after-opened', (ev, { isNewDoc }) => {
  24. if (isNewDoc) {
  25. // if a new doc is opened a cursor updates will be triggered before the
  26. // content is loaded. We have to ignore it or the outline highlight
  27. // will be incorrect. This doesn't happen when `doc:after-opened` is
  28. // fired without a new doc opened.
  29. this.ignoreNextCursorUpdate = true
  30. }
  31. // always ignore the next scroll update so the cursor update takes
  32. // precedence
  33. this.ignoreNextScroll = true
  34. this.shareJsDoc = scope.editor.sharejs_doc
  35. this.isTexFile = isValidTeXFile(scope.editor.open_doc_name)
  36. this.updateOutline()
  37. this.broadcastChangeEvent()
  38. })
  39. scope.$on('doc:changed', () => {
  40. this.updateOutline()
  41. this.broadcastChangeEvent()
  42. })
  43. scope.$on('file-view:file-opened', () => {
  44. this.isTexFile = false
  45. this.updateOutline()
  46. this.broadcastChangeEvent()
  47. })
  48. scope.$on('cursor:editor:update', (event, cursorPosition) => {
  49. if (this.ignoreNextCursorUpdate) {
  50. this.ignoreNextCursorUpdate = false
  51. return
  52. }
  53. this.updateHighlightedLine(cursorPosition.row + 1)
  54. this.broadcastChangeEvent()
  55. })
  56. scope.$on('scroll:editor:update', (event, middleVisibleRow) => {
  57. if (this.ignoreNextScroll) {
  58. this.ignoreNextScroll = false
  59. return
  60. }
  61. this.updateHighlightedLine(middleVisibleRow + 1)
  62. })
  63. scope.$watch('editor.showRichText', () => {
  64. this.ignoreNextScroll = true
  65. this.ignoreNextCursorUpdate = true
  66. if (this.shouldShowOutline()) {
  67. this.updateOutline()
  68. this.broadcastChangeEvent()
  69. }
  70. })
  71. }
  72. shouldShowOutline() {
  73. if (this.scope.editor.showRichText) {
  74. return true
  75. }
  76. return !this.scope.editor.newSourceEditor
  77. }
  78. updateOutline() {
  79. // Disable if using CM6
  80. if (!this.shouldShowOutline()) {
  81. return
  82. }
  83. if (this.isTexFile) {
  84. const content = this.ide.editorManager.getCurrentDocValue()
  85. if (content) {
  86. this.flatOutline = matchOutline(content)
  87. if (!isEqual(this.flatOutline, this.previousFlatOutline)) {
  88. this.previousFlatOutline = cloneDeep(this.flatOutline)
  89. this.outline = nestOutline(this.flatOutline)
  90. }
  91. return
  92. }
  93. }
  94. this.previousFlatOutline = []
  95. this.outline = []
  96. }
  97. // set highlightedLine to the closest outline line above the editorLine
  98. updateHighlightedLine(editorLine) {
  99. let closestOutlineLine = null
  100. for (let lineId = 0; lineId < this.flatOutline.length; lineId++) {
  101. const outline = this.flatOutline[lineId]
  102. if (editorLine < outline.line) break // editorLine is above
  103. closestOutlineLine = outline.line
  104. }
  105. if (closestOutlineLine === this.highlightedLine) return
  106. this.highlightedLine = closestOutlineLine
  107. this.broadcastChangeEvent()
  108. }
  109. jumpToLine(line, syncToPdf) {
  110. this.ignoreNextScroll = true
  111. this.ide.editorManager.jumpToLine({ gotoLine: line, syncToPdf })
  112. }
  113. broadcastChangeEvent() {
  114. // Disable if using CM6
  115. if (!this.shouldShowOutline()) {
  116. return
  117. }
  118. this.scope.$broadcast('outline-manager:outline-changed', {
  119. isTexFile: this.isTexFile,
  120. outline: this.outline,
  121. highlightedLine: this.highlightedLine,
  122. })
  123. }
  124. }
  125. export default OutlineManager