From c88f05d711ab7a3709f7536cccedda72d016ee13 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 6 Nov 2018 00:59:14 +0530 Subject: [PATCH] feat(Code Editor): Ace Editor for Code fieldtypes - Better editing experience with syntax highlighting and auto-completion - Support HTML, Javascript and CSS language modes via the Options field --- frappe/public/js/frappe/form/controls/code.js | 80 +++++++++++++++++-- frappe/public/less/common.less | 4 + package.json | 1 + yarn.lock | 5 ++ 4 files changed, 85 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/form/controls/code.js b/frappe/public/js/frappe/form/controls/code.js index db8b7d4bb2..4bdfae60a0 100644 --- a/frappe/public/js/frappe/form/controls/code.js +++ b/frappe/public/js/frappe/form/controls/code.js @@ -1,8 +1,78 @@ frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({ - make_input: function() { - this._super(); - $(this.input_area).find("textarea") - .allowTabs() - .addClass('control-code'); + make_input() { + if (this.editor) return; + this.load_lib().then(() => this.make_ace_editor()); + }, + + make_ace_editor() { + const ace_editor_target = $('
') + .appendTo(this.input_area); + + // styling + ace_editor_target.addClass('border rounded'); + ace_editor_target.css('height', 300); + + // initialize + this.editor = ace.edit(ace_editor_target.get(0)); + this.editor.setTheme('ace/theme/tomorrow'); + this.set_language(); + + // events + this.editor.session.on('change', frappe.utils.debounce((delta) => { + const input_value = this.get_input_value(); + this.parse_validate_and_set_in_model(input_value); + }, 300)); + }, + + set_language() { + const language_map = { + 'Javascript': 'ace/mode/javascript', + 'HTML': 'ace/mode/html', + 'CSS': 'ace/mode/css' + } + const language = this.df.options; + + const valid_languages = Object.keys(language_map); + if (!valid_languages.includes(language)) { + console.warn(`Invalid language option provided for field "${this.df.label}". Valid options are ${valid_languages.join(', ')}.`); + } + + const ace_language_mode = language_map[language] || ''; + this.editor.session.setMode(ace_language_mode); + }, + + parse(value) { + if (value == null) { + value = ""; + } + return value; + }, + + set_formatted_input(value) { + this.library_loaded.then(() => { + if (value === this.get_input_value()) return; + this.editor.session.setValue(value); + }); + }, + + get_input_value() { + return this.editor ? this.editor.session.getValue() : ''; + }, + + load_lib() { + if (frappe.boot.developer_mode) { + this.root_lib_path = '/assets/frappe/js/lib/ace-builds/src-noconflict/'; + } else { + this.root_lib_path = '/assets/frappe/js/lib/ace-builds/src-min-noconflict/'; + } + + this.library_loaded = new Promise(resolve => { + frappe.require(this.root_lib_path + 'ace.js', () => { + window.ace.config.set('basePath', this.root_lib_path); + resolve(); + }); + }); + + return this.library_loaded; } }); diff --git a/frappe/public/less/common.less b/frappe/public/less/common.less index 8d77191410..89e6c4a635 100644 --- a/frappe/public/less/common.less +++ b/frappe/public/less/common.less @@ -174,6 +174,10 @@ a.badge-hover& { border: 1px solid @border-color; } +.rounded { + border-radius: 4px; +} + .close-inline { font-size: 120%; font-weight: bold; diff --git a/package.json b/package.json index 984b382c6f..8737f9baef 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ }, "homepage": "https://frappe.io", "dependencies": { + "ace-builds": "^1.4.1", "awesomplete": "^1.1.2", "cookie": "^0.3.1", "express": "^4.16.2", diff --git a/yarn.lock b/yarn.lock index cff0ffc035..b95ec470b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -58,6 +58,11 @@ accepts@~1.3.4: mime-types "~2.1.16" negotiator "0.6.1" +ace-builds@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.4.1.tgz#a66e33bcd519918ee99bc54cac5d7399a83c8ff2" + integrity sha512-ulsfTOi/oAXBIt9hYlK4asyFZYsFOOUAbwBZidbjWWJycRX4LtwIithMyndcnoQ4PavkZ0iGJ+c9nqHNiYbubA== + acorn-dynamic-import@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz#901ceee4c7faaef7e07ad2a47e890675da50a278"