Skip to content

Commit

Permalink
feat: adding markdown-renderer service support
Browse files Browse the repository at this point in the history
  • Loading branch information
appelgriebsch committed Mar 17, 2019
1 parent e07996a commit 9ea684a
Show file tree
Hide file tree
Showing 7 changed files with 335 additions and 238 deletions.
12 changes: 9 additions & 3 deletions lib/main.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-check
/// <reference path="../typings/atom-ide.d.ts"/>
'use babel';

const { CompositeDisposable, Disposable } = require('atom');
Expand All @@ -25,7 +23,11 @@ module.exports = {

if (!this.signatureHelpManager) this.signatureHelpManager = new SignatureHelpManager();
this.subscriptions.add(this.signatureHelpManager);
this.signatureHelpManager.initialize();

require('atom-package-deps').install('atom-ide-signature-help')
.then(function() {
console.log('All dependencies installed, good to go')
});
},

deactivate() {
Expand All @@ -43,5 +45,9 @@ module.exports = {
return (provider) => {
return this.signatureHelpManager.addProvider(provider);
}
},

consumeMarkdownRenderer(renderer) {
this.signatureHelpManager.initialize(renderer);
}
};
36 changes: 26 additions & 10 deletions lib/signature-help-manager.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// @ts-check
/// <reference path="../typings/atom-ide.d.ts"/>
'use babel';

const { CompositeDisposable, Disposable, Point, Range, TextEditor } = require('atom');
Expand Down Expand Up @@ -49,12 +47,15 @@ module.exports = class SignatureHelpManager {
* @type {Boolean}
*/
this.showSignatureHelpOnTyping = false;

this.renderer = null;
}

initialize() {
initialize(renderer) {
this.subscriptions = new CompositeDisposable();
this.providerRegistry = new ProviderRegistry();
this.watchedEditors = new WeakSet();
this.renderer = renderer;

this.subscriptions.add(
atom.workspace.observeTextEditors(editor => {
Expand Down Expand Up @@ -121,9 +122,9 @@ module.exports = class SignatureHelpManager {
if (editorView.hasFocus()) {
this.updateCurrentEditor(editor);
}
let focusListener = (element) => this.updateCurrentEditor(editor);
let focusListener = () => this.updateCurrentEditor(editor);
editorView.addEventListener('focus', focusListener);
let blurListener = (element) => this.hideDataTip();
let blurListener = () => this.hideDataTip();
editorView.addEventListener('blur', blurListener);

let disposable = new Disposable(() => {
Expand Down Expand Up @@ -225,11 +226,27 @@ module.exports = class SignatureHelpManager {

if ((result === null) || (result.signatures.length === 0)) { return; }

console.log(result);

const index = result.activeSignature || 0;
const signature = result.signatures[index];
const signatureHelpView = new SignatureHelpView({ signature: signature, grammar: editor.getGrammar() });
this.signatureHelpDisposables = this.mountSignatureHelp(editor, position, signatureHelpView);
})

let markedString = new Array();
markedString.push(`<pre><code class="${editor.getGrammar().name.toLowerCase()}">${signature.label}</code></pre>`);

if (signature.documentation) {
const markdownDoc = signature.documentation.kind ? signature.documentation.value : signature.documentation;
markedString.push(markdownDoc);
}

const s = markedString.join('\r\n');
this.renderer.render(s).then((html) => {
const signatureHelpView = new SignatureHelpView({ htmlView: html });
this.signatureHelpDisposables = this.mountSignatureHelp(editor, position, signatureHelpView);
}).catch((err) => console.log(err));
});

console.log()
}

/**
Expand All @@ -241,7 +258,6 @@ module.exports = class SignatureHelpManager {
*/
mountSignatureHelp(editor, position, view) {
let disposables = new CompositeDisposable();

const overlayMarker = editor.markBufferRange(new Range(position, position), {
invalidate: 'never',
});
Expand All @@ -254,7 +270,7 @@ module.exports = class SignatureHelpManager {
});

view.element.style.display = 'block';
// move box above the current editing line
// move box above the current editing line
setTimeout(() => {
view.element.style.bottom = editor.getLineHeightInPixels() + view.element.getBoundingClientRect().height + 'px';
}, 100);
Expand Down
134 changes: 41 additions & 93 deletions lib/signature-help-view.js
Original file line number Diff line number Diff line change
@@ -1,101 +1,22 @@
'use babel';
/** @jsx etch.dom */

const etch = require('etch')
const marked = require('marked')
const createDOMPurify = require('dompurify')
const ReactDOMServer = require('react-dom/server');
const etch = require('etch');
const createDOMPurify = require('dompurify');
/**
* [domPurify description]
* @type {DOMPurify}
*/
const domPurify = createDOMPurify();

class SnippetView {
constructor({ snippet, grammar }) {
this.rootElement = document.createElement("div");
this.rootElement.className = "datatip-container";
if (snippet) {
this.refreshRootElement(snippet, grammar);
}
}

update({ snippet, grammar }) {
this.refreshRootElement(snippet, grammar);
}

refreshRootElement(snippet, grammar) {
const wrapper = document.createElement("div");
wrapper.className = "datatip-marked-text-editor";
if (snippet) {
const regExpLSPPrefix = /^\((method|property|parameter)\)\W/;
snippet = snippet.replace(regExpLSPPrefix, '');
if (grammar) {
const atomElem = atom.workspace.buildTextEditor({
lineNumberGutterVisible: false,
readonly: true,
keyboardInputEnabled: false,
showInvisibles: false,
tabLength: atom.config.get("editor.tabLength")
});
atomElem.setGrammar(grammar);
atomElem.setText(snippet);
const atomView = atom.views.getView(atomElem);
atomView.setUpdatedSynchronously(true);
atomView.style.pointerEvents = "none"
atomView.style.position = "absolute";
atomView.style.width = "0px";
atomView.style.height = "1px";
atom.views.getView(atom.workspace).appendChild(atomView);
this.editorTokenized(atomElem).then(() => {
const html = Array.from(atomView.querySelectorAll(".line:not(.dummy)"));
wrapper.innerHTML = domPurify.sanitize(html.map(x => x.innerHTML).join("\n"), { breaks: true });
atomView.remove();
}).catch(() => {
atomView.remove();
})
}
}
this.rootElement.appendChild(wrapper);
}

get element() {
return this.rootElement;
}

/**
* [editorTokenized description]
* @param {TextEditor} editor [description]
* @return {Promise} [description]
*/
editorTokenized(editor) {
return new Promise(resolve => {
if (editor.getBuffer().getLanguageMode().fullyTokenized) {
resolve()
} else {
const disp = editor.onDidTokenize(() => {
disp.dispose()
resolve()
})
}
})
}
}

class MarkdownView {
constructor({ markedString }) {
this.markedString = markedString;
class HtmlView {
constructor({ html }) {
this.rootElement = document.createElement('div');
this.rootElement.className = "datatip-marked-container";
this.rootElement.addEventListener("wheel", this.onMouseWheel, { passive: true });

if (this.markedString) {
this.rootElement.innerHTML = domPurify.sanitize(marked(this.markedString, { breaks: true }));
}
}
update({ markedString }) {
this.markedString = markedString;
if (this.markedString) {
this.rootElement.innerHTML = domPurify.sanitize(marked(this.markedString, { breaks: true }));
if (html){
this.rootElement.innerHTML = domPurify.sanitize(html);
}
}

Expand All @@ -112,6 +33,19 @@ class MarkdownView {
}
}

class ReactView {
constructor({ component }) {
this.rootElement = document.createElement('span')
if (component) {
this.rootElement.innerHTML = domPurify.sanitize(ReactDOMServer.renderToStaticMarkup(component()))
}
}

get element() {
return this.rootElement
}
}

module.exports = class SignatureHelpView {
// Required: Define an ordinary constructor to initialize your component.
constructor(properties) {
Expand All @@ -120,13 +54,27 @@ module.exports = class SignatureHelpView {
}

render() {
const documentation = this.properties.signature.documentation.value || this.properties.signature.documentation || null;
return (
<div className="datatip-element">
<SnippetView snippet={this.properties.signature.label} grammar={this.properties.grammar} />
<MarkdownView markedString={documentation} />
</div>
);
if (this.properties.reactView) {
return (
<div className="datatip-element">
<ReactView component={this.properties.reactView} />
</div>
);
}
else if (this.properties.htmlView) {
return (
<div className="datatip-element">
<HtmlView html={this.properties.htmlView} />
</div>
);
}
else {
return (
<div className="datatip-element">
{ this.children }
</div>
);
}
}

update(props, children) {
Expand Down
Loading

0 comments on commit 9ea684a

Please sign in to comment.