Codearea: Added tab-size and tab insertion to the widget

This commit is contained in:
Oracle 2025-08-11 18:35:09 +02:00
parent 9d7e1b080d
commit 9ee0798006
3 changed files with 109 additions and 7 deletions

View File

@ -11,7 +11,7 @@ class BaseModelAdmin( admin.ModelAdmin ):
def formfield_for_dbfield( self, db_field, **kwargs ):
formfield = super().formfield_for_dbfield(db_field, **kwargs)
if isinstance( formfield, JSONField ):
formfield.widget = Codearea( language = "json" )
formfield.widget = Codearea( language = "json-pretty" )
custom_widget = getattr(self, "field_widgets", {}).get(db_field.name, None)
if custom_widget:

View File

@ -35,6 +35,7 @@
padding: 5px;
font-size: 1em;
border-radius: 3px;
tab-size: {{ tab_size }};
}
</style>
@ -44,11 +45,65 @@
const textarea = codearea.querySelector('textarea');
const code = codearea.querySelector('code');
textarea.addEventListener('input', (e) => {
code.innerHTML = textarea.value;
//function encode(e){return e.replace(/[^]/g,function(e){return"&#"+e.charCodeAt(0)+";"})}
function sync(){
code.innerHTML = new Option(textarea.value).innerHTML; //encode(textarea.value);
delete code.dataset.highlighted;
hljs.highlightElement( code )
});
}
hljs.highlightElement( code )
textarea.addEventListener('keydown', function o(e){
if (e.key != 'Tab') return;
e.preventDefault();
//Largly From: https://stackoverflow.com/a/45396754
if (this.selectionStart == this.selectionEnd){
document.execCommand('insertText', false, "\t");
return;
}
// Block indent/unindent trashes undo stack.
// Select whole lines
var selStart = this.selectionStart;
var selEnd = this.selectionEnd;
var text = this.value;
while (selStart > 0 && text[selStart-1] != '\n')
selStart--;
while (selEnd > 0 && text[selEnd-1]!='\n' && selEnd < text.length)
selEnd++;
// Get selected text
var lines = text.substr(selStart, selEnd - selStart).split('\n');
// Insert tabs
for (var i=0; i<lines.length; i++)
{
// Don't indent last line if cursor at start of line
if (i==lines.length-1 && lines[i].length==0)
continue;
// Tab or Shift+Tab?
if (e.shiftKey)
{
if (lines[i].startsWith('\t'))
lines[i] = lines[i].substr(1);
else if (lines[i].startsWith(" "))
lines[i] = lines[i].substr(4);
}
else
lines[i] = "\t" + lines[i];
}
lines = lines.join('\n');
// Update the text area
this.value = text.substr(0, selStart) + lines + text.substr(selEnd);
this.selectionStart = selStart;
this.selectionEnd = selStart + lines.length;
sync();
})
textarea.addEventListener('input', () => sync() );
})()
</script>

View File

@ -1,18 +1,65 @@
from django.template import loader
from django.forms import Textarea
import json
class Codearea( Textarea ):
def __init__( self, *args, language = "auto", **kwargs ):
def __init__( self,
*args,
language = "auto",
spell_check = False,
extra_rows = 2, extra_cols = 10,
min_rows = 10, min_cols = 40,
tab_size = 4,
**kwargs
):
self.language = language
self.spell_check = spell_check
self.extra_rows = extra_rows
self.extra_cols = extra_cols
self.min_rows = min_rows
self.min_cols = min_cols
self.tab_size = tab_size
super().__init__( *args, **kwargs )
def render( self, name, value, attrs = None, renderer = None ):
if not self.spell_check:
attrs.update({
"autocomplete": "off",
"autocorrect": "off",
"autocapitalize": "off",
"spellcheck": "false"
})
value = value or ""
if self.language == "json-pretty":
self.language = "json"
value = json.dumps(json.loads(value), indent = 4 )
if self.extra_rows is not False:
attrs.update(
rows = max( value.count("\n") + self.extra_rows, self.min_rows )
)
if self.extra_cols is not False:
try:
max_line_chars = max( len(line) + line.count("\t") * (self.tab_size - 1) for line in value.split("\n") )
except ValueError:
max_line_chars = 0
attrs.update(
cols = max( max_line_chars + self.extra_cols, self.min_cols )
)
return loader.get_template("asyncron/codearea-widget.html").render({
"textarea": super().render( name, value, attrs, renderer ),
"value": value,
"target": name,
"language": self.language
"tab_size": self.tab_size,
"language": self.language,
})