diff --git a/src/htmlMode.ts b/src/htmlMode.ts index 29204df7..da96f530 100644 --- a/src/htmlMode.ts +++ b/src/htmlMode.ts @@ -23,9 +23,12 @@ export function setupMode(defaults: LanguageServiceDefaultsImpl): void { // all modes monaco.languages.registerCompletionItemProvider(languageId, new languageFeatures.CompletionAdapter(worker)); + monaco.languages.registerHoverProvider(languageId, new languageFeatures.HoverAdapter(worker)); + monaco.languages.registerDocumentHighlightProvider(languageId, new languageFeatures.DocumentHighlightAdapter(worker)); monaco.languages.registerLinkProvider(languageId, new languageFeatures.DocumentLinkAdapter(worker)); monaco.languages.registerFoldingRangeProvider(languageId, new languageFeatures.FoldingRangeAdapter(worker)); + monaco.languages.registerDocumentSymbolProvider(languageId, new languageFeatures.DocumentSymbolAdapter(worker)); // only html if (languageId === 'html') { diff --git a/src/htmlWorker.ts b/src/htmlWorker.ts index 130e649d..23014475 100644 --- a/src/htmlWorker.ts +++ b/src/htmlWorker.ts @@ -40,6 +40,12 @@ export class HTMLWorker { let textEdits = this._languageService.format(document, range, this._languageSettings && this._languageSettings.format); return Promise.resolve(textEdits); } + doHover(uri: string, position: ls.Position): Thenable { + let document = this._getTextDocument(uri); + let htmlDocument = this._languageService.parseHTMLDocument(document); + let hover = this._languageService.doHover(document, position, htmlDocument); + return Promise.resolve(hover); + } findDocumentHighlights(uri: string, position: ls.Position): Thenable { let document = this._getTextDocument(uri); let htmlDocument = this._languageService.parseHTMLDocument(document); @@ -51,6 +57,12 @@ export class HTMLWorker { let links = this._languageService.findDocumentLinks(document, null); return Promise.resolve(links); } + findDocumentSymbols(uri: string): Thenable { + let document = this._getTextDocument(uri); + let htmlDocument = this._languageService.parseHTMLDocument(document); + let symbols = this._languageService.findDocumentSymbols(document, htmlDocument); + return Promise.resolve(symbols); + } provideFoldingRanges(uri: string, context?: { rangeLimit?: number; }): Thenable { let document = this._getTextDocument(uri); let ranges = this._languageService.getFoldingRanges(document, context); diff --git a/src/languageFeatures.ts b/src/languageFeatures.ts index 3ae813ee..8950044c 100644 --- a/src/languageFeatures.ts +++ b/src/languageFeatures.ts @@ -265,6 +265,66 @@ export class CompletionAdapter implements monaco.languages.CompletionItemProvide } } +// --- hover ------ + +function isMarkupContent(thing: any): thing is ls.MarkupContent { + return thing && typeof thing === 'object' && typeof (thing).kind === 'string'; +} + +function toMarkdownString(entry: ls.MarkupContent | ls.MarkedString): monaco.IMarkdownString { + if (typeof entry === 'string') { + return { + value: entry + }; + } + if (isMarkupContent(entry)) { + if (entry.kind === 'plaintext') { + return { + value: entry.value.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') + }; + } + return { + value: entry.value + }; + } + + return { value: '```' + entry.language + '\n' + entry.value + '\n```\n' }; +} + +function toMarkedStringArray(contents: ls.MarkupContent | ls.MarkedString | ls.MarkedString[]): monaco.IMarkdownString[] { + if (!contents) { + return void 0; + } + if (Array.isArray(contents)) { + return contents.map(toMarkdownString); + } + return [toMarkdownString(contents)]; +} + +export class HoverAdapter implements monaco.languages.HoverProvider { + + constructor(private _worker: WorkerAccessor) { + } + + provideHover(model: monaco.editor.IReadOnlyModel, position: Position, token: CancellationToken): Thenable { + let resource = model.uri; + + return this._worker(resource).then(worker => { + return worker.doHover(resource.toString(), fromPosition(position)); + }).then(info => { + if (!info) { + return; + } + return { + range: toRange(info.range), + contents: toMarkedStringArray(info.contents) + }; + }); + } +} + +// --- document highlights ------ + function toHighlighKind(kind: ls.DocumentHighlightKind): monaco.languages.DocumentHighlightKind { const mKind = monaco.languages.DocumentHighlightKind; @@ -297,6 +357,58 @@ export class DocumentHighlightAdapter implements monaco.languages.DocumentHighli } } +// --- document symbols ------ + +function toSymbolKind(kind: ls.SymbolKind): monaco.languages.SymbolKind { + let mKind = monaco.languages.SymbolKind; + + switch (kind) { + case ls.SymbolKind.File: return mKind.Array; + case ls.SymbolKind.Module: return mKind.Module; + case ls.SymbolKind.Namespace: return mKind.Namespace; + case ls.SymbolKind.Package: return mKind.Package; + case ls.SymbolKind.Class: return mKind.Class; + case ls.SymbolKind.Method: return mKind.Method; + case ls.SymbolKind.Property: return mKind.Property; + case ls.SymbolKind.Field: return mKind.Field; + case ls.SymbolKind.Constructor: return mKind.Constructor; + case ls.SymbolKind.Enum: return mKind.Enum; + case ls.SymbolKind.Interface: return mKind.Interface; + case ls.SymbolKind.Function: return mKind.Function; + case ls.SymbolKind.Variable: return mKind.Variable; + case ls.SymbolKind.Constant: return mKind.Constant; + case ls.SymbolKind.String: return mKind.String; + case ls.SymbolKind.Number: return mKind.Number; + case ls.SymbolKind.Boolean: return mKind.Boolean; + case ls.SymbolKind.Array: return mKind.Array; + } + return mKind.Function; +} + +export class DocumentSymbolAdapter implements monaco.languages.DocumentSymbolProvider { + + constructor(private _worker: WorkerAccessor) { + } + + public provideDocumentSymbols(model: monaco.editor.IReadOnlyModel, token: CancellationToken): Thenable { + const resource = model.uri; + + return this._worker(resource).then(worker => worker.findDocumentSymbols(resource.toString())).then(items => { + if (!items) { + return; + } + return items.map(item => ({ + name: item.name, + detail: '', + containerName: item.containerName, + kind: toSymbolKind(item.kind), + range: toRange(item.location.range), + selectionRange: toRange(item.location.range) + })); + }); + } +} + export class DocumentLinkAdapter implements monaco.languages.LinkProvider { constructor(private _worker: WorkerAccessor) {