outline-manager.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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.$on('doc:after-opened', (ev, { isNewDoc }) => {
  18. if (isNewDoc) {
  19. // if a new doc is opened a cursor updates will be triggered before the
  20. // content is loaded. We have to ignore it or the outline highlight
  21. // will be incorrect. This doesn't happen when `doc:after-opened` is
  22. // fired without a new doc opened.
  23. this.ignoreNextCursorUpdate = true
  24. }
  25. // always ignore the next scroll update so the cursor update takes
  26. // precedence
  27. this.ignoreNextScroll = true
  28. this.shareJsDoc = scope.editor.sharejs_doc
  29. this.isTexFile = isValidTeXFile(scope.editor.open_doc_name)
  30. this.updateOutline()
  31. this.broadcastChangeEvent()
  32. })
  33. scope.$on('doc:changed', () => {
  34. this.updateOutline()
  35. this.broadcastChangeEvent()
  36. })
  37. scope.$on('file-view:file-opened', () => {
  38. this.isTexFile = false
  39. this.updateOutline()
  40. this.broadcastChangeEvent()
  41. })
  42. scope.$on('cursor:editor:update', (event, cursorPosition) => {
  43. if (this.ignoreNextCursorUpdate) {
  44. this.ignoreNextCursorUpdate = false
  45. return
  46. }
  47. this.updateHighlightedLine(cursorPosition.row + 1)
  48. this.broadcastChangeEvent()
  49. })
  50. scope.$on('scroll:editor:update', (event, middleVisibleRow) => {
  51. if (this.ignoreNextScroll) {
  52. this.ignoreNextScroll = false
  53. return
  54. }
  55. this.updateHighlightedLine(middleVisibleRow + 1)
  56. })
  57. scope.$watch('editor.showRichText', () => {
  58. this.ignoreNextScroll = true
  59. this.ignoreNextCursorUpdate = true
  60. })
  61. }
  62. updateOutline() {
  63. if (this.isTexFile) {
  64. const content = this.ide.editorManager.getCurrentDocValue()
  65. if (content) {
  66. this.flatOutline = matchOutline(content)
  67. if (!isEqual(this.flatOutline, this.previousFlatOutline)) {
  68. this.previousFlatOutline = cloneDeep(this.flatOutline)
  69. this.outline = nestOutline(this.flatOutline)
  70. }
  71. return
  72. }
  73. }
  74. this.previousFlatOutline = []
  75. this.outline = []
  76. }
  77. // set highlightedLine to the closest outline line above the editorLine
  78. updateHighlightedLine(editorLine) {
  79. let closestOutlineLine = null
  80. for (let lineId = 0; lineId < this.flatOutline.length; lineId++) {
  81. const outline = this.flatOutline[lineId]
  82. if (editorLine < outline.line) break // editorLine is above
  83. closestOutlineLine = outline.line
  84. }
  85. if (closestOutlineLine === this.highlightedLine) return
  86. this.highlightedLine = closestOutlineLine
  87. this.broadcastChangeEvent()
  88. }
  89. jumpToLine(line, syncToPdf) {
  90. this.ignoreNextScroll = true
  91. this.ide.editorManager.jumpToLine({ gotoLine: line, syncToPdf })
  92. }
  93. broadcastChangeEvent() {
  94. this.scope.$broadcast('outline-manager:outline-changed', {
  95. isTexFile: this.isTexFile,
  96. outline: this.outline,
  97. highlightedLine: this.highlightedLine,
  98. })
  99. }
  100. }
  101. export default OutlineManager