lspLanguageFeatures.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import * as monaco from 'monaco-editor'
  2. import type { LogWorker } from './worker/logWorker'
  3. export type WorkerAccessor<T> = (...uris: monaco.Uri[]) => Promise<T>
  4. export abstract class Adapter {
  5. constructor(protected _worker: (...uris: monaco.Uri[]) => Promise<LogWorker>) {}
  6. }
  7. //#region DiagnosticsAdapter
  8. export interface ILanguageWorkerWithDiagnostics {
  9. doValidation(uri: string): Promise<monaco.editor.IMarkerData[]>
  10. }
  11. export class DiagnosticsAdapter<T extends ILanguageWorkerWithDiagnostics> {
  12. protected readonly _disposables: monaco.IDisposable[] = []
  13. private readonly _listener: { [uri: string]: monaco.IDisposable } = Object.create(null)
  14. constructor(
  15. private readonly _languageId: string,
  16. protected readonly _worker: WorkerAccessor<T>,
  17. ) {
  18. const onModelAdd = (model: monaco.editor.IModel): void => {
  19. let modeId = model.getLanguageId()
  20. if (modeId !== this._languageId) {
  21. return
  22. }
  23. let handle: number
  24. this._listener[model.uri.toString()] = model.onDidChangeContent(() => {
  25. window.clearTimeout(handle)
  26. handle = window.setTimeout(() => this._doValidate(model.uri, modeId), 500)
  27. })
  28. this._doValidate(model.uri, modeId)
  29. }
  30. const onModelRemoved = (model: monaco.editor.IModel): void => {
  31. monaco.editor.setModelMarkers(model, this._languageId, [])
  32. let uriStr = model.uri.toString()
  33. let listener = this._listener[uriStr]
  34. if (listener) {
  35. listener.dispose()
  36. delete this._listener[uriStr]
  37. }
  38. }
  39. this._disposables.push(monaco.editor.onDidCreateModel(onModelAdd))
  40. this._disposables.push(monaco.editor.onWillDisposeModel(onModelRemoved))
  41. this._disposables.push(
  42. monaco.editor.onDidChangeModelLanguage((event) => {
  43. onModelRemoved(event.model)
  44. onModelAdd(event.model)
  45. }),
  46. )
  47. this._disposables.push({
  48. dispose: () => {
  49. monaco.editor.getModels().forEach(onModelRemoved)
  50. for (let key in this._listener) {
  51. this._listener[key].dispose()
  52. }
  53. },
  54. })
  55. monaco.editor.getModels().forEach(onModelAdd)
  56. }
  57. public dispose(): void {
  58. this._disposables.forEach((d) => d && d.dispose())
  59. this._disposables.length = 0
  60. }
  61. private _doValidate(resource: monaco.Uri, languageId: string): void {
  62. this._worker(resource)
  63. .then((worker) => {
  64. return worker.doValidation(resource.toString())
  65. })
  66. .then((diagnostics) => {
  67. const markers = diagnostics
  68. let model = monaco.editor.getModel(resource)
  69. if (model && model.getLanguageId() === languageId) {
  70. monaco.editor.setModelMarkers(model, 'LogWorker', markers)
  71. }
  72. })
  73. .then(undefined, (err) => {
  74. console.error(err)
  75. })
  76. }
  77. }
  78. // --- inlay hints ----
  79. export class InlayHintsAdapter extends Adapter implements monaco.languages.InlayHintsProvider {
  80. public async provideInlayHints(
  81. model: monaco.editor.ITextModel,
  82. range: monaco.Range,
  83. token: monaco.CancellationToken,
  84. ): Promise<monaco.languages.InlayHintList | null> {
  85. const resource = model.uri
  86. const fileName = resource.toString()
  87. const start = model.getOffsetAt({
  88. lineNumber: range.startLineNumber,
  89. column: range.startColumn,
  90. })
  91. const end = model.getOffsetAt({
  92. lineNumber: range.endLineNumber,
  93. column: range.endColumn,
  94. })
  95. const worker = await this._worker(resource)
  96. if (model.isDisposed()) {
  97. return null
  98. }
  99. const logHints = await worker.provideInlayHints(fileName, start, end)
  100. const hints: monaco.languages.InlayHint[] = logHints.map((hint) => {
  101. return {
  102. ...hint,
  103. label: hint.label,
  104. position: hint.position,
  105. kind: hint.kind,
  106. }
  107. })
  108. return { hints, dispose: () => {} }
  109. }
  110. }