Merge pull request #12888 from netchampfaris/server-script-autocompletion

feat: Add autocompletion items in Server Script
This commit is contained in:
mergify[bot] 2021-04-20 16:50:21 +00:00 committed by GitHub
commit bdbdef92a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 105 additions and 2 deletions

View file

@ -18,7 +18,7 @@ global_cache_keys = ("app_hooks", "installed_apps", 'all_apps',
'scheduler_events', 'time_zone', 'webhooks', 'active_domains',
'active_modules', 'assignment_rule', 'server_script_map', 'wkhtmltopdf_version',
'domain_restricted_doctypes', 'domain_restricted_pages', 'information_schema:counts',
'sitemap_routes', 'db_tables') + doctype_map_keys
'sitemap_routes', 'db_tables', 'server_script_autocompletion_items') + doctype_map_keys
user_cache_keys = ("bootinfo", "user_recent", "roles", "user_doc", "lang",
"defaults", "user_permissions", "home_page", "linked_with",

View file

@ -9,6 +9,12 @@ frappe.ui.form.on('Server Script', {
if (frm.doc.script_type != 'Scheduler Event') {
frm.dashboard.hide();
}
frm.call('get_autocompletion_items')
.then(r => r.message)
.then(items => {
frm.set_df_property('script', 'autocompletions', items);
});
},
setup_help(frm) {

View file

@ -5,11 +5,12 @@
from __future__ import unicode_literals
import ast
from types import FunctionType, MethodType, ModuleType
from typing import Dict, List
import frappe
from frappe.model.document import Document
from frappe.utils.safe_exec import safe_exec
from frappe.utils.safe_exec import get_safe_globals, safe_exec, NamespaceDict
from frappe import _
@ -122,6 +123,51 @@ class ServerScript(Document):
if locals["conditions"]:
return locals["conditions"]
@frappe.whitelist()
def get_autocompletion_items(self):
"""Generates a list of a autocompletion strings from the context dict
that is used while executing a Server Script.
Returns:
list: Returns list of autocompletion items.
For e.g., ["frappe.utils.cint", "frappe.db.get_all", ...]
"""
def get_keys(obj):
out = []
for key in obj:
if key.startswith('_'):
continue
value = obj[key]
if isinstance(value, (NamespaceDict, dict)) and value:
if key == 'form_dict':
out.append(['form_dict', 7])
continue
for subkey, score in get_keys(value):
fullkey = f'{key}.{subkey}'
out.append([fullkey, score])
else:
if isinstance(value, type) and issubclass(value, Exception):
score = 0
elif isinstance(value, ModuleType):
score = 10
elif isinstance(value, (FunctionType, MethodType)):
score = 9
elif isinstance(value, type):
score = 8
elif isinstance(value, dict):
score = 7
else:
score = 6
out.append([key, score])
return out
items = frappe.cache().get_value('server_script_autocompletion_items')
if not items:
items = get_keys(get_safe_globals())
items = [{'value': d[0], 'score': d[1]} for d in items]
frappe.cache().set_value('server_script_autocompletion_items', items)
return items
@frappe.whitelist()
def setup_scheduler_events(script_name, frequency):

View file

@ -31,6 +31,57 @@ frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({
const input_value = this.get_input_value();
this.parse_validate_and_set_in_model(input_value);
}, 300));
// setup autocompletion when it is set the first time
Object.defineProperty(this.df, 'autocompletions', {
get() {
return this._autocompletions || [];
},
set: (value) => {
this.setup_autocompletion();
this.df._autocompletions = value;
}
});
},
setup_autocompletion() {
if (this._autocompletion_setup) return;
const ace = window.ace;
const get_autocompletions = () => this.df.autocompletions;
ace.config.loadModule("ace/ext/language_tools", langTools => {
this.editor.setOptions({
enableBasicAutocompletion: true,
enableLiveAutocompletion: true
});
langTools.addCompleter({
getCompletions: function(editor, session, pos, prefix, callback) {
if (prefix.length === 0) {
callback(null, []);
return;
}
let autocompletions = get_autocompletions();
if (autocompletions.length) {
callback(
null,
autocompletions.map(a => {
if (typeof a === 'string') {
a = { value: a };
}
return {
name: 'frappe',
value: a.value,
score: a.score
};
})
);
}
}
});
});
this._autocompletion_setup = true;
},
refresh_height() {