Skip to content

Instantly share code, notes, and snippets.

@mwrouse
Last active April 25, 2024 08:47
Show Gist options
  • Star 47 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save mwrouse/05d8c11cd3872c19c684bd1904a2202e to your computer and use it in GitHub Desktop.
Save mwrouse/05d8c11cd3872c19c684bd1904a2202e to your computer and use it in GitHub Desktop.
Autocompletion for an object in the monaco editor
function ShowAutocompletion(obj) {
// Disable default autocompletion for javascript
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({ noLib: true });
// Helper function to return the monaco completion item type of a thing
function getType(thing, isMember) {
isMember = (isMember == undefined) ? (typeof isMember == "boolean") ? isMember : false : false; // Give isMember a default value of false
switch ((typeof thing).toLowerCase()) {
case "object":
return monaco.languages.CompletionItemKind.Class;
case "function":
return (isMember) ? monaco.languages.CompletionItemKind.Method : monaco.languages.CompletionItemKind.Function;
default:
return (isMember) ? monaco.languages.CompletionItemKind.Property : monaco.languages.CompletionItemKind.Variable;
}
}
// Register object that will return autocomplete items
monaco.languages.registerCompletionItemProvider('javascript', {
// Run this function when the period or open parenthesis is typed (and anything after a space)
triggerCharacters: ['.', '('],
// Function to generate autocompletion results
provideCompletionItems: function(model, position, token) {
// Split everything the user has typed on the current line up at each space, and only look at the last word
var last_chars = model.getValueInRange({startLineNumber: position.lineNumber, startColumn: 0, endLineNumber: position.lineNumber, endColumn: position.column});
var words = last_chars.replace("\t", "").split(" ");
var active_typing = words[words.length - 1]; // What the user is currently typing (everything after the last space)
// This if statement adds support for autocomplete inside if statements and stuff
if (active_typing.includes('(')) {
active_typing = active_typing.split('(');
active_typing = active_typing[active_typing.length - 1];
}
// If the last character typed is a period then we need to look at member objects of the obj object
var is_member = active_typing.charAt(active_typing.length - 1) == ".";
// Array of autocompletion results
var result = [];
// Used for generic handling between member and non-member objects
var last_token = obj;
var prefix = '';
if (is_member) {
// Is a member, get a list of all members, and the prefix
var parents = active_typing.substring(0, active_typing.length - 1).split(".");
last_token = obj[parents[0]];
prefix = parents[0];
// Loop through all the parents the current one will have (to generate prefix)
for (var i = 1; i < parents.length; i++) {
var propToLookFor = parents[i];
// Support for arrays
var isPropAnArray = propToLookFor.charAt(propToLookFor.length - 1) == ']';
if (isPropAnArray)
propToLookFor = propToLookFor.split('[')[0];
if (last_token.hasOwnProperty(propToLookFor)) {
prefix += '.' + propToLookFor;
last_token = last_token[propToLookFor];
if (isPropAnArray && Array.isArray(last_token))
{
last_token = last_token[0];
}
} else {
// Not valid
return result;
}
}
prefix += '.';
}
// Array properties
if (Array.isArray(last_token))
last_token = { length: 0 };
// Get all the child properties of the last token
for (var prop in last_token) {
// Do not show properites that begin with "__"
if (last_token.hasOwnProperty(prop) && !prop.startsWith("__")) {
// Get the detail type (try-catch) incase object does not have prototype
var details = '';
try {
details = last_token[prop].__proto__.constructor.name;
} catch (e) {
details = typeof last_token[prop];
}
// Create completion object
var to_push = {
label: prefix + prop,
kind: getType(last_token[prop], is_member),
detail: details,
insertText: prop
};
// Change insertText and documentation for functions
if (to_push.detail.toLowerCase() == 'function') {
to_push.insertText += "(";
to_push.documentation = (last_token[prop].toString()).split("{")[0]; // Show function prototype in the documentation popup
}
// Add to final results
result.push(to_push);
}
}
return {
suggestions: result
};
}
});
}
// This simple example will show autocompletion for "Person" with the given properties of name, age
ShowAutocompletion({
Person: {
name: "",
age: 0
}
});
@Haigos
Copy link

Haigos commented Nov 8, 2022

Just came across this, exactly what I needed. Thx for sharing!

@kankk
Copy link

kankk commented Nov 14, 2023

var code = 'const person = ' + JSON.stringify(yourObject); var libUri = 'ts:filename/facts.d.ts'; monaco.languages.typescript.typescriptDefaults.addExtraLib(code, libUri); monaco.editor.createModel(code, 'typescript', monaco.Uri.parse(libUri));

:)

This is the recommended usage of monaco-editor, but it cannot solve the variable autocompletion on this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment