@@ -449,7 +449,7 @@ var ICEcoder = {
449
449
thisCM . getLine ( thisCMPrevLine ) &&
450
450
thisCM . getLine ( thisCMPrevLine ) . length > 0 &&
451
451
thisCM . getLine ( thisCMPrevLine ) . replace ( / \s / g, '' ) . length === 0 ) {
452
- thisCM . replaceRange ( "" , { line : thisCMPrevLine , ch : 0 } , { line : thisCMPrevLine , ch : 1000000 } ) ;
452
+ thisCM . replaceRange ( "" , { line : thisCMPrevLine , ch : 0 } , { line : thisCMPrevLine , ch : 1000000 } , "+input" ) ;
453
453
}
454
454
455
455
// Set the cursor to text height, not line height
@@ -565,7 +565,7 @@ var ICEcoder = {
565
565
566
566
// Replace our string over the range, if this token string isn't blank and the end tag matches our original tag
567
567
if ( "" !== theTag . trim ( ) && "undefined" !== typeof repl1 && "undefined" !== typeof repl2 && thisCM . getRange ( repl1 , repl2 ) === rData [ 0 ] ) {
568
- thisCM . replaceRange ( theTag , repl1 , repl2 ) ;
568
+ thisCM . replaceRange ( theTag , repl1 , repl2 , "+input" ) ;
569
569
// If at the close tag, don't autocomplete
570
570
if ( tagInfo . at === "close" ) {
571
571
this . autocompleteSkip = true ;
@@ -1110,16 +1110,16 @@ var ICEcoder = {
1110
1110
// Move lines in turn up
1111
1111
if ( "up" === dir ) {
1112
1112
for ( let i = lineStart . line ; i <= lineEnd . line ; i ++ ) {
1113
- thisCM . replaceRange ( thisCM . getLine ( i ) , { line : i - 1 , ch : 0 } , { line : i - 1 , ch : 1000000 } ) ;
1113
+ thisCM . replaceRange ( thisCM . getLine ( i ) , { line : i - 1 , ch : 0 } , { line : i - 1 , ch : 1000000 } , "+input" ) ;
1114
1114
}
1115
1115
// ...or down
1116
1116
} else {
1117
1117
for ( let i = lineEnd . line ; i >= lineStart . line ; i -- ) {
1118
- thisCM . replaceRange ( thisCM . getLine ( i ) , { line : i + 1 , ch : 0 } , { line : i + 1 , ch : 1000000 } ) ;
1118
+ thisCM . replaceRange ( thisCM . getLine ( i ) , { line : i + 1 , ch : 0 } , { line : i + 1 , ch : 1000000 } , "+input" ) ;
1119
1119
}
1120
1120
}
1121
1121
// Now swap our final line with the swap line to complete the move
1122
- thisCM . replaceRange ( swapLine , { line : "up" === dir ? lineEnd . line : lineStart . line , ch : 0 } , { line : "up" === dir ? lineEnd . line : lineStart . line , ch : 1000000 } ) ;
1122
+ thisCM . replaceRange ( swapLine , { line : "up" === dir ? lineEnd . line : lineStart . line , ch : 0 } , { line : "up" === dir ? lineEnd . line : lineStart . line , ch : 1000000 } , "+input" ) ;
1123
1123
// Finally set the moved selection
1124
1124
thisCM . setSelection (
1125
1125
{ line : lineStart . line + ( "up" === dir ? - 1 : 1 ) , ch : lineStart . ch } ,
@@ -1243,7 +1243,7 @@ var ICEcoder = {
1243
1243
thisCM = this . getThisCM ( ) ;
1244
1244
1245
1245
if ( ! line ) { line = thisCM . getCursor ( ) . line }
1246
- thisCM . replaceRange ( thisCM . getLine ( line ) + "<br>" , { line : line , ch : 0 } , { line : line , ch : 1000000 } ) ;
1246
+ thisCM . replaceRange ( thisCM . getLine ( line ) + "<br>" , { line : line , ch : 0 } , { line : line , ch : 1000000 } , "+input" ) ;
1247
1247
} ,
1248
1248
1249
1249
// Insert a line before and auto-indent
@@ -1254,7 +1254,7 @@ var ICEcoder = {
1254
1254
1255
1255
if ( ! line ) { line = thisCM . getCursor ( ) . line }
1256
1256
thisCM . operation ( function ( ) {
1257
- thisCM . replaceRange ( "\n" + thisCM . getLine ( line ) , { line : line , ch : 0 } , { line : line , ch : 1000000 } ) ;
1257
+ thisCM . replaceRange ( "\n" + thisCM . getLine ( line ) , { line : line , ch : 0 } , { line : line , ch : 1000000 } , "+input" ) ;
1258
1258
thisCM . setCursor ( { line : thisCM . getCursor ( ) . line - 1 , ch : 0 } ) ;
1259
1259
thisCM . execCommand ( 'indentAuto' ) ;
1260
1260
} ) ;
@@ -1268,7 +1268,7 @@ var ICEcoder = {
1268
1268
1269
1269
if ( ! line ) { line = thisCM . getCursor ( ) . line }
1270
1270
thisCM . operation ( function ( ) {
1271
- thisCM . replaceRange ( thisCM . getLine ( line ) + "\n" , { line : line , ch : 0 } , { line : line , ch : 1000000 } ) ;
1271
+ thisCM . replaceRange ( thisCM . getLine ( line ) + "\n" , { line : line , ch : 0 } , { line : line , ch : 1000000 } , "+input" ) ;
1272
1272
thisCM . execCommand ( 'indentAuto' ) ;
1273
1273
} ) ;
1274
1274
} ,
@@ -1288,7 +1288,7 @@ var ICEcoder = {
1288
1288
} else {
1289
1289
if ( ! line ) { line = thisCM . getCursor ( ) . line }
1290
1290
ch = thisCM . getCursor ( ) . ch ;
1291
- thisCM . replaceRange ( thisCM . getLine ( line ) + "\n" + thisCM . getLine ( line ) , { line : line , ch : 0 } , { line : line , ch : 1000000 } ) ;
1291
+ thisCM . replaceRange ( thisCM . getLine ( line ) + "\n" + thisCM . getLine ( line ) , { line : line , ch : 0 } , { line : line , ch : 1000000 } , "+input" ) ;
1292
1292
thisCM . setCursor ( line + 1 , ch ) ;
1293
1293
}
1294
1294
} ,
@@ -1426,6 +1426,38 @@ var ICEcoder = {
1426
1426
}
1427
1427
} ,
1428
1428
1429
+ // Return character num from start of doc to cursor
1430
+ getCharNumFromCursor : function ( ) {
1431
+ return this . getThisCM ( ) . getRange ( { line : 0 , ch : 0 } , this . getThisCM ( ) . getCursor ( ) ) . length ;
1432
+ } ,
1433
+
1434
+ // Set the cursor according to num of characters from start of doc
1435
+ setCursorByCharNum : function ( num ) {
1436
+ // Temp data store
1437
+ this . charPos = {
1438
+ len : 0 ,
1439
+ thisLine : 0 ,
1440
+ thisChar : 0
1441
+ } ;
1442
+ // For each line in editor
1443
+ this . getThisCM ( ) . eachLine ( function ( line ) {
1444
+ // The number we're seeking if greater than prev linees we've considered plus this line
1445
+ if ( num > ICEcoder . charPos . len + ( line . text + "\n" ) . length ) {
1446
+ // Increment line
1447
+ ICEcoder . charPos . thisLine ++ ;
1448
+ // It's equal to or greater than the number we're seeking, so on this line
1449
+ } else if ( ICEcoder . charPos . thisChar === 0 ) {
1450
+ // Set char (to avoid setting more than once) and set cursor
1451
+ ICEcoder . charPos . thisChar = num - ICEcoder . charPos . len ;
1452
+ ICEcoder . getThisCM ( ) . setCursor ( { line : ICEcoder . charPos . thisLine , ch : ICEcoder . charPos . thisChar } )
1453
+ }
1454
+ // Build up length count
1455
+ ICEcoder . charPos . len += ( line . text + "\n" ) . length ;
1456
+ } ) ;
1457
+ // Remove temp data store
1458
+ delete this . charPos ;
1459
+ } ,
1460
+
1429
1461
// Determine which area of the document we're in
1430
1462
caretLocationType : function ( ) {
1431
1463
let thisCM , caretLocType , caretChunk , fileName , fileExt ;
@@ -1519,7 +1551,7 @@ var ICEcoder = {
1519
1551
for ( let i = startLine ; i <= endLine ; i ++ ) {
1520
1552
cM . replaceRange ( cM . getLine ( i ) . slice ( 0 , commentCH . length ) != commentCH
1521
1553
? commentCH + cM . getLine ( i )
1522
- : cM . getLine ( i ) . slice ( commentCH . length , cM . getLine ( i ) . length ) , { line :i , ch :0 } , { line :i , ch :1000000 } ) ;
1554
+ : cM . getLine ( i ) . slice ( commentCH . length , cM . getLine ( i ) . length ) , { line :i , ch :0 } , { line :i , ch :1000000 } , "+input" ) ;
1523
1555
}
1524
1556
// Language has block commenting
1525
1557
} else {
@@ -1532,13 +1564,13 @@ var ICEcoder = {
1532
1564
if ( - 1 < [ "CSS" , "SQL" ] . indexOf ( this . caretLocType ) ) {
1533
1565
cM . replaceRange ( lineContent . slice ( 0 , commentBS . length ) != commentBS
1534
1566
? commentBS + lineContent + commentBE
1535
- : lineContent . slice ( commentBS . length , lCLen - commentBE . length ) , { line : linePos , ch : 0 } , { line : linePos , ch : 1000000 } ) ;
1567
+ : lineContent . slice ( commentBS . length , lCLen - commentBE . length ) , { line : linePos , ch : 0 } , { line : linePos , ch : 1000000 } , "+input" ) ;
1536
1568
adjustCursor = commentBS . length ;
1537
1569
if ( lineContent . slice ( 0 , commentBS . length ) == commentBS ) { adjustCursor = - adjustCursor }
1538
1570
} else {
1539
1571
cM . replaceRange ( lineContent . slice ( 0 , commentCH . length ) != commentCH
1540
1572
? commentCH + lineContent
1541
- : lineContent . slice ( commentCH . length , lCLen ) , { line : linePos , ch : 0 } , { line : linePos , ch : 1000000 } ) ;
1573
+ : lineContent . slice ( commentCH . length , lCLen ) , { line : linePos , ch : 0 } , { line : linePos , ch : 1000000 } , "+input" ) ;
1542
1574
adjustCursor = commentCH . length ;
1543
1575
if ( lineContent . slice ( 0 , commentCH . length ) == commentCH ) { adjustCursor = - adjustCursor }
1544
1576
}
@@ -1552,7 +1584,7 @@ var ICEcoder = {
1552
1584
} else {
1553
1585
cM . replaceRange ( lineContent . slice ( 0 , 4 ) !== "<\!--"
1554
1586
? "<\!--" + lineContent + "//-->"
1555
- : lineContent . slice ( 4 , lCLen - 5 ) , { line : linePos , ch : 0 } , { line : linePos , ch : 1000000 } ) ;
1587
+ : lineContent . slice ( 4 , lCLen - 5 ) , { line : linePos , ch : 0 } , { line : linePos , ch : 1000000 } , "+input" ) ;
1556
1588
adjustCursor = lineContent . slice ( 0 , 4 ) === "<\!--" ? - 4 : 4 ;
1557
1589
}
1558
1590
}
@@ -2004,6 +2036,7 @@ var ICEcoder = {
2004
2036
// Save a file
2005
2037
saveFile : function ( saveAs , newFileAutoSave ) {
2006
2038
let changes , saveType , filePath , fileExt , pathPrefix ;
2039
+ let prettierVersion , editorText , prettierText , sm , opcodes , docShift , startShift , endShift , newContent ;
2007
2040
filePath = this . openFiles [ this . selectedTab - 1 ] ;
2008
2041
fileExt = filePath . substr ( filePath . lastIndexOf ( "." ) + 1 ) ;
2009
2042
if ( "undefined" !== typeof prettier && [ "js" , "json" , "ts" , "css" , "scss" , "less" , "html" , "xml" , "yaml" , "md" , "php" ] . indexOf ( fileExt ) > - 1 ) {
@@ -2021,15 +2054,74 @@ var ICEcoder = {
2021
2054
case "php" : parser = "php" ; break ;
2022
2055
}
2023
2056
try {
2024
- this . getThisCM ( ) . setValue ( prettier . format (
2057
+ prettierVersion = prettier . formatWithCursor (
2025
2058
this . getThisCM ( ) . getValue ( ) ,
2026
2059
{
2027
2060
parser : parser ,
2028
2061
plugins : prettierPlugins ,
2029
2062
tabWidth : this . indentSize ,
2030
- useTabs : "tabs" === this . indentType
2063
+ useTabs : "tabs" === this . indentType ,
2064
+ cursorOffset : this . getCharNumFromCursor ( )
2065
+ }
2066
+ ) ;
2067
+
2068
+ // Get the text values and split it into lines
2069
+ editorText = difflib . stringAsLines ( this . getThisCM ( ) . getValue ( ) ) ;
2070
+ prettierText = difflib . stringAsLines ( prettierVersion . formatted ) ;
2071
+
2072
+ // Create a SequenceMatcher instance that diffs the two sets of lines
2073
+ sm = new difflib . SequenceMatcher ( editorText , prettierText ) ;
2074
+
2075
+ // Get the opcodes from the SequenceMatcher instance
2076
+ // Opcodes is a list of 3-tuples describing what changes should be made to the base text in order to yield the new text
2077
+ opcodes = sm . get_opcodes ( ) ;
2078
+ docShift = 0 ;
2079
+
2080
+ for ( let i = 0 ; i < opcodes . length ; i ++ ) {
2081
+ // opcode events may be:
2082
+ // equal = do nothing for this range
2083
+ // replace = replace [1]-[2] with [3]-[4]
2084
+ // insert = replace [1]-[2] with [3]-[4]
2085
+ // delete = replace [1]-[2] with [3]-[4]
2086
+ // Params to determine if we need to set 1 or 0 shift the start line and end line
2087
+ startShift = "delete" === opcodes [ i ] [ 0 ] && editorText . length === opcodes [ i ] [ 2 ] ? 1 : 0 ;
2088
+ endShift = "replace" === opcodes [ i ] [ 0 ] ? 1 : 0 ;
2089
+ if ( "equal" !== opcodes [ i ] [ 0 ] ) {
2090
+ // Replace or insert
2091
+ if ( "replace" === opcodes [ i ] [ 0 ] || "insert" === opcodes [ i ] [ 0 ] ) {
2092
+ newContent = "" ;
2093
+ // For each of the replace/insert lines in Prettier's version
2094
+ for ( let j = opcodes [ i ] [ 3 ] ; j < opcodes [ i ] [ 4 ] ; j ++ ) {
2095
+ // Build up newContent lines and end with a new line char if not the last line in the range
2096
+ newContent += prettierText [ j ] ;
2097
+ if ( j < opcodes [ i ] [ 4 ] - 1 ) {
2098
+ newContent += "\n" ;
2099
+ }
2100
+ }
2101
+ }
2102
+ // Delete
2103
+ if ( "delete" === opcodes [ i ] [ 0 ] ) {
2104
+ // Not the last line in doc, the newContent is the line after the section we're deleting in editors version
2105
+ // Else if it's the last line in doc, the content after the section we're deleting is nothing
2106
+ newContent = editorText . length > opcodes [ i ] [ 2 ]
2107
+ ? editorText [ opcodes [ i ] [ 2 ] ]
2108
+ : "" ;
2109
+ }
2110
+ console . log ( startShift ) ;
2111
+ // Replace the range with newContent. The range start line and end line adjust addording to
2112
+ // startShift and endShift 1/0 values plus also the +/- docShift which is how much the
2113
+ // editor document has shifted so far during replace ranges
2114
+ this . getThisCM ( ) . replaceRange ( newContent , { line : opcodes [ i ] [ 1 ] - docShift - startShift , ch : 0 } , { line : opcodes [ i ] [ 2 ] - docShift - endShift , ch : 1000000 } , "+input" ) ;
2115
+ // Work out the +/- document shift based on difference between the editors last line in
2116
+ // this diff range and Prettiers last line in this diff range
2117
+ docShift = opcodes [ i ] [ 2 ] - opcodes [ i ] [ 4 ] ;
2031
2118
}
2032
- ) ) ;
2119
+ }
2120
+ // If we don't have text selected, we have a cursor, so move the cursor to new place in
2121
+ // the prettified version now we've made adjustments
2122
+ if ( false === this . getThisCM ( ) . somethingSelected ( ) ) {
2123
+ this . setCursorByCharNum ( prettierVersion . cursorOffset ) ;
2124
+ }
2033
2125
} catch ( err ) {
2034
2126
get ( "toolLinkOutput" ) . className = "highlight error" ;
2035
2127
this . outputMsg ( '<div style="background: #b00; padding: 1px 3px; border-radius: 3px; font-family: monospace;">Syntax error in ' + this . openFiles [ this . selectedTab - 1 ] . replace ( iceRoot , "" ) + '</div>\n' + err . message . replace ( / < / g, '<' ) . replace ( / > / g, '>' ) ) ;
@@ -3174,7 +3266,7 @@ var ICEcoder = {
3174
3266
thisCM = this . getThisCM ( ) ;
3175
3267
3176
3268
cursor = thisCM . getTokenAt ( thisCM . getCursor ( ) ) ;
3177
- thisCM . replaceRange ( color , { line :thisCM . getCursor ( ) . line , ch :cursor . start } , { line :thisCM . getCursor ( ) . line , ch :cursor . end } ) ;
3269
+ thisCM . replaceRange ( color , { line :thisCM . getCursor ( ) . line , ch :cursor . start } , { line :thisCM . getCursor ( ) . line , ch :cursor . end } , "+input" ) ;
3178
3270
} ,
3179
3271
3180
3272
// Change opacity of the file manager icons
@@ -4812,7 +4904,7 @@ var ICEcoder = {
4812
4904
}
4813
4905
}
4814
4906
// Clear the cursor string and set the cursor there
4815
- thisCM . replaceRange ( replacedLine . replace ( "CURSOR" , "" ) , { line :lineNo , ch :0 } , { line :lineNo , ch :1000000 } ) ;
4907
+ thisCM . replaceRange ( replacedLine . replace ( "CURSOR" , "" ) , { line :lineNo , ch :0 } , { line :lineNo , ch :1000000 } , "+input" ) ;
4816
4908
thisCM . setCursor ( lineNoCount , curPos ) ;
4817
4909
// Finally, focus on the editor
4818
4910
this . focus ( this . editorFocusInstance . indexOf ( 'diff' ) > - 1 ? true : false ) ;
@@ -5103,10 +5195,10 @@ var ICEcoder = {
5103
5195
// Push a duplicate of tail onto end, to increase snake length by 1 block
5104
5196
this . snakePos . push ( [ this . snakePos [ this . snakePos . length - 1 ] [ 0 ] , this . snakePos [ this . snakePos . length - 1 ] [ 1 ] ] ) ;
5105
5197
// Replace char under head with nothing if end of line, else with our replacement string
5106
- cM . doc . replaceRange ( this . snakePos [ 0 ] [ 0 ] - 1 == lineContent . length - 2 ? "" : spaceReplaceChars , lineData , { line : lineData . line , ch : lineData . ch + 1 } ) ;
5198
+ cM . doc . replaceRange ( this . snakePos [ 0 ] [ 0 ] - 1 == lineContent . length - 2 ? "" : spaceReplaceChars , lineData , { line : lineData . line , ch : lineData . ch + 1 } , "+input" ) ;
5107
5199
// Remove any trailing space at end
5108
5200
if ( this . snakePos [ 0 ] [ 0 ] - 1 == lineContent . length - 2 ) {
5109
- cM . doc . replaceRange ( cM . getLine ( lineData . line ) . replace ( / [ \t ] + $ / , '' ) , { line : lineData . line , ch : 0 } , { line : lineData . line , ch : 1000000 } ) ;
5201
+ cM . doc . replaceRange ( cM . getLine ( lineData . line ) . replace ( / [ \t ] + $ / , '' ) , { line : lineData . line , ch : 0 } , { line : lineData . line , ch : 1000000 } , "+input" ) ;
5110
5202
}
5111
5203
} else {
5112
5204
// Reduce snake length if over 5 chars and not on content
0 commit comments