Skip to content

Commit

Permalink
ux(editor): Poking around completer API for custom rendering
Browse files Browse the repository at this point in the history
Just Poking around to see how to get our custom rendering function.
It's a bit dumb as you need to pass the render function for each
completion item and not to as a global render function.

I guess that could be patched in codemirror upstream, but let's do this
for now.

From the UI-side of things it should not change much, but that should
allow to test new completer UI for the extra information that can be
returned from ipykernel (that needs patches).

documentation and style

Handle metadata unset.
  • Loading branch information
Carreau authored and rgbkrk committed Jun 1, 2017
1 parent 0eb6ca3 commit e4e56e9
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 11 deletions.
8 changes: 3 additions & 5 deletions packages/editor/__tests__/editor-spec.js
Expand Up @@ -101,11 +101,9 @@ describe("complete", () => {
// Listen on the Observable
observable.subscribe(
msg => {
expect(msg).toEqual({
from: { line: 3, ch: 9 },
list: ["import this"],
to: { ch: 10, line: 3 }
});
expect(msg.from).toEqual({ line: 3, ch: 9 });
expect(msg.list[0].text).toEqual("import this");
expect(msg.to).toEqual({ ch: 10, line: 3 });
},
err => {
throw err;
Expand Down
66 changes: 60 additions & 6 deletions packages/editor/src/complete.js
Expand Up @@ -12,18 +12,72 @@ export function formChangeObject(cm, change) {
};
}

// ipykernel may return experimental completion in the metadata field,
// experiment with these. We use codemirror ability to take a rendering function
// on a per completion basis (we can't give a global one :-( to render not only
// the text, but the type as well.
// as this is not documented in CM the DOM structure of the completer will be
//
// <ul class="CodeMirror-hints" >
// <li class="CodeMirror-hint"></li>
// <li class="CodeMirror-hint CodeMirror-hint-active"></li>
// <li class="CodeMirror-hint"></li>
// <li class="CodeMirror-hint"></li>
// </ul>
// with each <li/> passed as the first argument of render.
const _expand_experimental_completions = (editor, matches, cursor) => ({
to: cursor,
from: cursor,
list: matches.map(completion => ({
text: completion.text,
to: editor.posFromIndex(completion.end),
from: editor.posFromIndex(completion.start),
type: completion.type,
render: (elt, data, completion) => {
const span = document.createElement("span");
const text = document.createTextNode(completion.text);
span.className += "completion-type completion-type-" + completion.type;
span.setAttribute("title", completion.type);
elt.appendChild(span);
elt.appendChild(text);
}
}))
});

// duplicate of default codemirror rendering logic for completions,
// except if the completion have a metadata._experimental key, dispatch to a new
// completer for these new values.
export const expand_completions = editor => results => {
if ((results.metadata || {})._jupyter_types_experimental != undefined) {
try {
return _expand_experimental_completions(
editor,
results.metadata._jupyter_types_experimental,
editor.getCursor()
);
} catch (e) {
console.error("Exprimental completion failed :", e);
}
}
return {
list: results.matches.map(match => ({
text: match,
render: (elt, data, current) =>
elt.appendChild(document.createTextNode(current.text))
})),
from: editor.posFromIndex(results.cursor_start),
to: editor.posFromIndex(results.cursor_end)
};
};

export function codeCompleteObservable(channels, editor, message) {
const completion$ = channels.shell
.childOf(message)
.ofMessageType(["complete_reply"])
.pluck("content")
.first()
.map(results => ({
list: results.matches,
from: editor.posFromIndex(results.cursor_start),
to: editor.posFromIndex(results.cursor_end)
}))
.timeout(2000); // 4s
.map(expand_completions(editor))
.timeout(2000); // 2s

// On subscription, send the message
return Rx.Observable.create(observer => {
Expand Down
67 changes: 67 additions & 0 deletions static/styles/main.css
Expand Up @@ -44,6 +44,73 @@ div#loading {
Globals
*/

/* completions styles */

.CodeMirror-hint {
padding-left: 0;
border-bottom: none;
}

.completion-type {
background: transparent;
border:transparent 1px solid;
width: 17px;
height: 17px;
margin: 0;
padding: 0;
display: inline-block;
margin-right: 5px;
top: 18px;
}

.completion-type:before {
content: "?";
bottom: 1px;
left: 4px;
position: relative;
}
/* color and content for each type of completion */
.completion-type-keyword:before { content: "K"; }
.completion-type-keyword{
background-color: darkred;
}

.completion-type-class:before { content: "C"; }
.completion-type-class{
background-color: blueviolet;
}

.completion-type-module:before { content: "M"; }
.completion-type-module{
background-color: chocolate;
}

.completion-type-statement:before { content: "S"; }
.completion-type-statement{
background-color: forestgreen;
}

.completion-type-function:before { content: "ƒ"; }
.completion-type-function{
background-color: yellowgreen;
}

.completion-type-instance:before { content: "I"; }
.completion-type-instance{
background-color: teal;
}

.completion-type-null:before { content: "ø"; }
.completion-type-null{
background-color: black;
}


/* end completion type color and content */




.octicon {
transition: color 0.5s;
}
Expand Down

0 comments on commit e4e56e9

Please sign in to comment.