editor-split
Fabien Benetou 1 year ago
parent b8cdd97aac
commit 5240ab9ff9
  1. 121
      index.html

@ -2214,27 +2214,23 @@ function startExperience(){
document.querySelector("#mainbutton").style.display = "none"
}
let codeEditor
// should consider multiple instances instead
let codeEditor = {
element: null,
line: 0,
page: null,
startWindowRange: 0,
lengthWindowRange: 20,
scrollInterval: null,
currentlyDisplayedText: "",
caret: null,
language: null,
}
function nextLineCodeEditor(lines=1){ // can be negative to scroll up
let editors = []
// track created editors then apply actions to the currently selected one
// problems happen when relying on querySelector/getElementById rather than a specific editor
// do these based on codeEditor.element rather than document
// still probably problematic for interactions
// consider for now only the currentEditor
function nextLineCodeEditor(codeEditor, lines=1){ // can be negative to scroll up
if (codeEditor.line+lines < 0) return
codeEditor.line+=lines
let content=codeEditor.page.split("\n").slice(codeEditor.line,codeEditor.line+codeEditor.lengthWindowRange).join("\n");
codeEditor.currentlyDisplayedText=content
codeEditor.element.setAttribute("troika-text", {value: content})
if (codeEditor.language) codeEditor.element.setAttribute("troika-text", {colorRanges: highlight(content, language='javascript')})
let gutterEl = document.getElementById("leftgutter")
let gutterEl = codeEditor.element.querySelector(".leftgutter")
if (gutterEl){
let lineNumbers = "\n"
for (let i=codeEditor.line+1;i<=codeEditor.line+codeEditor.lengthWindowRange;i++){
@ -2245,7 +2241,7 @@ function nextLineCodeEditor(lines=1){ // can be negative to scroll up
gutterEl.setAttribute("troika-text", {value: lineNumbers})
}
let rightGutterEl = document.getElementById("rightgutter")
let rightGutterEl = codeEditor.element.querySelector(".rightgutter")
if (rightGutterEl){
b = rightGutterEl.parentElement.object3D.children[0]._textRenderInfo.blockBounds
w = b[2]-b[0]
@ -2257,23 +2253,23 @@ function nextLineCodeEditor(lines=1){ // can be negative to scroll up
}
}
function nextPageCodeEditor(){
nextLineCodeEditor(codeEditor.lengthWindowRange)
function nextPageCodeEditor(codeEditor, ){
nextLineCodeEditor(codeEditor, codeEditor.lengthWindowRange)
}
function previousPageCodeEditor(){
nextLineCodeEditor(-codeEditor.lengthWindowRange)
function previousPageCodeEditor(codeEditor, ){
nextLineCodeEditor(codeEditor, -codeEditor.lengthWindowRange)
}
function stopScrollCodeEditor(){
function stopScrollCodeEditor(codeEditor, ){
codeEditor.scrollInterval = clearInterval( codeEditor.scrollInterval )
}
function startScrollCodeEditor(){
if (!codeEditor.scrollInterval) codeEditor.scrollInterval = setInterval( _ => nextLineCodeEditor(), 100)
function startScrollCodeEditor(codeEditor, ){
if (!codeEditor.scrollInterval) codeEditor.scrollInterval = setInterval( _ => nextLineCodeEditor(codeEditor, ), 100)
}
function highlightAllOccurences(keyword="function"){
function highlightAllOccurences(codeEditor, keyword="function"){
let indices = []
let lastfound = codeEditor.currentlyDisplayedText.indexOf(keyword,0)
while (lastfound>-1) {
@ -2287,7 +2283,7 @@ function highlightAllOccurences(keyword="function"){
})
}
function hightlightNextKeyword(keyword="function"){
function hightlightNextKeyword(codeEditor, keyword="function"){
let pos = codeEditor.currentlyDisplayedText.indexOf(keyword)
// invisible characters... some still left
let offset = (codeEditor.currentlyDisplayedText.slice(0,pos).match(/[\n\t ]/g)||[]).length
@ -2295,13 +2291,13 @@ function hightlightNextKeyword(keyword="function"){
highlightString(pos, keyword.length)
}
function highlightString(pos, length){
for (let c=pos;c<pos+length;c++) highlightChar( c )
function highlightString(codeEditor, pos, length){
for (let c=pos;c<pos+length;c++) highlightChar(codeEditor, c )
}
// WARNING this is limited to visible characters, i.e not " " or "\t" or "\n"
// should instead allow to highlight " " and "\t" both looking the same
function highlightChar(pos=0, name=null){ // could have multiple selection
function highlightChar(codeEditor, pos=0, name=null){ // could have multiple selection
let b = Array.from( codeEditor.element.object3D.children[0].geometry.attributes.aTroikaGlyphBounds.array ).slice(pos*4,pos*4+4)
let w = b[2]-b[0]
let h = b[3]-b[1] // could be used to check for same line, if so could make a single block from beginning to end
@ -2317,7 +2313,7 @@ function highlightChar(pos=0, name=null){ // could have multiple selection
c.position.z= .01
}
function highlightUnderChar(pos=0, name=null){
function highlightUnderChar(codeEditor, pos=0, name=null){
let b = Array.from( codeEditor.element.object3D.children[0].geometry.attributes.aTroikaGlyphBounds.array ).slice(pos*4,pos*4+4)
// note that this skips invisible char and thus desync codeEditor.caret from actual position
// but, for now at least, " " and "\t" seems to be of equal value and "\n" does not shift on the current line
@ -2350,45 +2346,45 @@ function highlightUnderChar(pos=0, name=null){
c.position.z= .01
}
function moveCaretToNextVisibleChar(){
function moveCaretToNextVisibleChar(codeEditor, ){
addCaretToCodeEditor( ++codeEditor.caret )
// might be able to reach non visible one by remove an offset
}
function addCaretToCodeEditor(pos=0){
if (codeEditor.caret) removeCaretFromCodeEditor()
highlightUnderChar(pos, "caret")
function addCaretToCodeEditor(codeEditor, pos=0){
if (codeEditor.caret) removeCaretFromCodeEditor(codeEditor, )
highlightUnderChar(codeEditor, pos, "caret")
codeEditor.caret = pos
}
function removeCaretFromCodeEditor(){
function removeCaretFromCodeEditor(codeEditor, ){
codeEditor.element.object3D.getObjectByName("caret").removeFromParent()
}
function clearCodeEditorContent(){
function clearCodeEditorContent(codeEditor, ){
updateCodeEditorWithContent( "" )
}
// should support a range, note the entire document (or window?)
function searchAndReplaceInCodeEditor(before, after){
updateCodeEditorWithContent( codeEditor.currentlyDisplayedText.replaceAll(before, after))
function searchAndReplaceInCodeEditor(codeEditor, before, after){
updateCodeEditorWithContent( codeEditor, codeEditor.currentlyDisplayedText.replaceAll(before, after))
// note that it desyncs from page so page should only be seen as the initial source
// this though would break scrolling which is based on page
// consequently page should be modified
}
function updateCodeEditorWithContent(content){
function updateCodeEditorWithContent(codeEditor, content){
if (!codeEditor.element) return
codeEditor.currentlyDisplayedText=content
codeEditor.element.setAttribute("troika-text", {value: content})
if (codeEditor.language) codeEditor.element.setAttribute("troika-text", {colorRanges: highlight(content, language='javascript')})
}
function addBackdropToTroikaElement( el ){
function addBackdropToTroikaElement( codeEditor, el ){
el.addEventListener("object3dset", e => {
el.object3D.children[0].addEventListener("synccomplete", e => {
// this can be used for resizing but without add the element
if (document.getElementById("leftgutter")) return // already added, should unregister
if (codeEditor.element.querySelector(".leftgutter")) return // already added, should unregister
b = el.object3D.children[0]._textRenderInfo.blockBounds
w = b[2]-b[0]
@ -2405,10 +2401,10 @@ function addBackdropToTroikaElement( el ){
})
}
function addGuttersToTroikaElement( el ){
function addGuttersToTroikaElement( codeEditor, el ){
el.addEventListener("object3dset", e => {
el.object3D.children[0].addEventListener("synccomplete", e => {
if (document.getElementById("leftgutter")) return
if (codeEditor.element.querySelector(".leftgutter")) return
// already added, should unregister, can be removed to allow dynamic resizing BUT should skip adding element
b = el.object3D.children[0]._textRenderInfo.blockBounds
@ -2421,7 +2417,6 @@ function addGuttersToTroikaElement( el ){
m = new THREE.MeshBasicMaterial( {color: 0x333333, opacity: 0.9, transparent: true} );
c = new THREE.Mesh( g, m );
el.object3D.add( c );
c.name = "leftgutter"
c.position.z=-.01
c.position.x= -gutterWidth/2
//c.rotation.y= .2 // looks nice but have to consider text on top first, could apply rotation to text too
@ -2438,7 +2433,7 @@ function addGuttersToTroikaElement( el ){
lineNumbers.slice(0,-1)
leftGutter.setAttribute("troika-text", {value: lineNumbers})
leftGutter.setAttribute("troika-text", {textIndent: -.5})
leftGutter.id = "leftgutter"
leftGutter.className = "leftgutter"
codeEditor.element.appendChild( leftGutter )
// should be updated when scrolling
@ -2447,19 +2442,18 @@ function addGuttersToTroikaElement( el ){
m = new THREE.MeshBasicMaterial( {color: 0x333333, opacity: 0.9, transparent: true} );
c = new THREE.Mesh( g, m );
el.object3D.add( c );
c.name = "rightgutter"
c.position.z=-.01
c.position.x= w+gutterWidth/2
//c.rotation.y= -.2 // looks nice but have to consider text on top first
var rightGutter = document.createElement("a-cylinder")
// height proportional to the visible content to the terminal size
let scrollBarHeight = codeEditor.lengthWindowRange/getNumberOfLinesFromCodeEditor() * h
let scrollBarVerticalOffset = codeEditor.line/getNumberOfLinesFromCodeEditor() * h
let scrollBarHeight = codeEditor.lengthWindowRange/getNumberOfLinesFromCodeEditor(codeEditor, ) * h
let scrollBarVerticalOffset = codeEditor.line/getNumberOfLinesFromCodeEditor(codeEditor, ) * h
if (scrollBarHeight < .1) scrollBarHeight = .1
rightGutter.setAttribute("height", scrollBarHeight )
rightGutter.setAttribute("radius", .01 )
rightGutter.id = "rightgutter"
rightGutter.className = "rightgutter"
// should become a constrained target (moving only on y axis and clamped)
codeEditor.element.appendChild( rightGutter )
// so... rightgutter vs rightGutter ... somehow changing to the "correct" one breaks the editor itself (?!)
@ -2472,7 +2466,6 @@ function addGuttersToTroikaElement( el ){
m = new THREE.MeshBasicMaterial( {color: 0x333333, opacity: 0.9, transparent: true} );
c = new THREE.Mesh( g, m );
el.object3D.add( c );
c.name = "middlegutter"
c.position.z=-.01
c.position.y= -h/2-gutterHeight/2
c.position.x= w/2
@ -2484,7 +2477,7 @@ function addGuttersToTroikaElement( el ){
middleGutter.setAttribute("outline-color", "black" )
middleGutter.setAttribute("troika-text", {value: ":(will add commands here)"})
//middleGutter.setAttribute("troika-text", {textIndent: -.3})
middleGutter.id = "middlegutter"
middleGutter.className = "middlegutter"
codeEditor.element.appendChild( middleGutter )
middleGutter.object3D.position.y= -h/2-gutterHeight/2
// should disable the overlay first, see parseKeys
@ -2517,7 +2510,7 @@ function addGuttersToTroikaElement( el ){
})
}
function getNumberOfLinesFromCodeEditor(){
function getNumberOfLinesFromCodeEditor(codeEditor, ){
let newLines = codeEditor.page.match(/\n/g)
if (!newLines) return 1 // undefined or 0
return newLines.length
@ -2528,6 +2521,17 @@ function getNumberOfLinesFromCodeEditor(){
// should also support clipboard or even a more direct way to have impact
// could save remotely (e.g wiki) or locally in localStorage
function addCodeEditor(page="jxr console.log('hello world')", language="javascript", position="-.5 1.6 -.7", name="codeditor" ){
let codeEditor = {
element: null,
line: 0,
page: null,
startWindowRange: 0,
lengthWindowRange: 20,
scrollInterval: null,
currentlyDisplayedText: "",
caret: null,
language: null,
}
// could also add empty but with column and row for sizing
// for now supporting only 1 scode editor, despite the name parameter
@ -2550,7 +2554,7 @@ function addCodeEditor(page="jxr console.log('hello world')", language="javascri
codeEditor.page = forcedLines
codeEditor.line = codeEditor.startWindowRange
let numberOfLines = getNumberOfLinesFromCodeEditor()
let numberOfLines = getNumberOfLinesFromCodeEditor(codeEditor, )
if (numberOfLines<codeEditor.lengthWindowRange) codeEditor.lengthWindowRange = numberOfLines
content=codeEditor.page.split("\n").slice(codeEditor.line,codeEditor.line+codeEditor.lengthWindowRange).join("\n");
@ -2566,7 +2570,7 @@ function addCodeEditor(page="jxr console.log('hello world')", language="javascri
codeEditor.language = language
}
addBackdropToTroikaElement( codeEditor.element )
addBackdropToTroikaElement( codeEditor, codeEditor.element )
//addGuttersToTroikaElement( codeEditor.element )
let scrollbarPicked = false
@ -2592,13 +2596,13 @@ function addCodeEditor(page="jxr console.log('hello world')", language="javascri
}
p.addEventListener('pinchstarted', pinchPrimaryScrollbarStarted );
function pinchPrimaryScrollbarStarted(event){
let rightGutterEl = document.getElementById("rightgutter")
let rightGutterEl = codeEditor.element.querySelector(".rightgutter")
previousPosition = event.detail.position.clone()
rightGutterEl.object3D.getWorldPosition( target );
if (previousPosition.distanceTo(target)<0.1) scrollbarPicked = true
}
return codeEditor.element
return codeEditor
}
// could change model opacity based on hand position, fading out when within a (very small here) safe space
@ -3041,7 +3045,14 @@ function tiltId(id, value){
// used for testing
AFRAME.registerComponent('startfunctions', {
init: function () {
fetch( 'https://webdav.benetou.fr/fot-demo-day/mobydick-extract.txt').then(r=>r.text()).then( page => { addCodeEditor( page, '', '-0.22 1.4 -.4' ) })
fetch( 'https://webdav.benetou.fr/fot-demo-day/mobydick-extract.txt').then(r=>r.text()).then( src => {
let parts = 4 // can't handle odd splits...
let pl = src.length/parts
for (let n=0; n<parts; n++ ){
addCodeEditor( src.slice(n*pl,(n+1)*pl), '', '-0.22 '+(2-n/10)+' -.4' )
}
})
// should become a component instead
//startExperience()
//doublePinchToScale()

Loading…
Cancel
Save