11import PropTypes from 'prop-types' ;
22import React from 'react' ;
33import CodeMirror from 'codemirror' ;
4- import beautifyJS from 'js-beautify' ;
4+ import emmet from '@emmetio/codemirror-plugin' ;
5+ import prettier from 'prettier' ;
6+ import babelParser from 'prettier/parser-babel' ;
7+ import htmlParser from 'prettier/parser-html' ;
8+ import cssParser from 'prettier/parser-postcss' ;
59import { withTranslation } from 'react-i18next' ;
610import 'codemirror/mode/css/css' ;
711import 'codemirror/addon/selection/active-line' ;
@@ -56,15 +60,12 @@ import * as UserActions from '../../User/actions';
5660import * as ToastActions from '../actions/toast' ;
5761import * as ConsoleActions from '../actions/console' ;
5862
59- const beautifyCSS = beautifyJS . css ;
60- const beautifyHTML = beautifyJS . html ;
63+ emmet ( CodeMirror ) ;
6164
6265window . JSHINT = JSHINT ;
6366window . CSSLint = CSSLint ;
6467window . HTMLHint = HTMLHint ;
65- delete CodeMirror . keyMap . sublime [ 'Shift-Tab' ] ;
6668
67- const IS_TAB_INDENT = false ;
6869const INDENTATION_AMOUNT = 2 ;
6970
7071class Editor extends React . Component {
@@ -84,8 +85,6 @@ class Editor extends React.Component {
8485 }
8586 } , 2000 ) ;
8687 this . showFind = this . showFind . bind ( this ) ;
87- this . findNext = this . findNext . bind ( this ) ;
88- this . findPrev = this . findPrev . bind ( this ) ;
8988 this . showReplace = this . showReplace . bind ( this ) ;
9089 this . getContent = this . getContent . bind ( this ) ;
9190 }
@@ -107,6 +106,11 @@ class Editor extends React.Component {
107106 keyMap : 'sublime' ,
108107 highlightSelectionMatches : true , // highlight current search match
109108 matchBrackets : true ,
109+ emmet : {
110+ preview : [ 'html' ] ,
111+ markTagPairs : true ,
112+ autoRenameTags : true
113+ } ,
110114 autoCloseBrackets : this . props . autocloseBracketsQuotes ,
111115 styleSelectedText : true ,
112116 lint : {
@@ -129,6 +133,7 @@ class Editor extends React.Component {
129133 metaKey === 'Ctrl' ? `${ metaKey } -H` : `${ metaKey } -Option-F` ;
130134 this . _cm . setOption ( 'extraKeys' , {
131135 Tab : ( cm ) => {
136+ if ( ! cm . execCommand ( 'emmetExpandAbbreviation' ) ) return ;
132137 // might need to specify and indent more?
133138 const selection = cm . doc . getSelection ( ) ;
134139 if ( selection . length > 0 ) {
@@ -137,11 +142,13 @@ class Editor extends React.Component {
137142 cm . replaceSelection ( ' ' . repeat ( INDENTATION_AMOUNT ) ) ;
138143 }
139144 } ,
145+ Enter : 'emmetInsertLineBreak' ,
146+ Esc : 'emmetResetAbbreviation' ,
140147 [ `${ metaKey } -Enter` ] : ( ) => null ,
141148 [ `Shift-${ metaKey } -Enter` ] : ( ) => null ,
142149 [ `${ metaKey } -F` ] : 'findPersistent' ,
143- [ `${ metaKey } -G` ] : 'findNext ' ,
144- [ `Shift-${ metaKey } -G` ] : 'findPrev ' ,
150+ [ `${ metaKey } -G` ] : 'findPersistentNext ' ,
151+ [ `Shift-${ metaKey } -G` ] : 'findPersistentPrev ' ,
145152 [ replaceCommand ] : 'replace'
146153 } ) ;
147154
@@ -168,8 +175,14 @@ class Editor extends React.Component {
168175 } ) ;
169176
170177 this . _cm . on ( 'keydown' , ( _cm , e ) => {
171- // 9 === Tab
172- if ( e . keyCode === 9 && e . shiftKey ) {
178+ // 70 === f
179+ if (
180+ ( ( metaKey === 'Cmd' && e . metaKey ) ||
181+ ( metaKey === 'Ctrl' && e . ctrlKey ) ) &&
182+ e . shiftKey &&
183+ e . keyCode === 70
184+ ) {
185+ e . preventDefault ( ) ;
173186 this . tidyCode ( ) ;
174187 }
175188 } ) ;
@@ -181,8 +194,6 @@ class Editor extends React.Component {
181194 this . props . provideController ( {
182195 tidyCode : this . tidyCode ,
183196 showFind : this . showFind ,
184- findNext : this . findNext ,
185- findPrev : this . findPrev ,
186197 showReplace : this . showReplace ,
187198 getContent : this . getContent
188199 } ) ;
@@ -302,16 +313,6 @@ class Editor extends React.Component {
302313 return updatedFile ;
303314 }
304315
305- findPrev ( ) {
306- this . _cm . focus ( ) ;
307- this . _cm . execCommand ( 'findPrev' ) ;
308- }
309-
310- findNext ( ) {
311- this . _cm . focus ( ) ;
312- this . _cm . execCommand ( 'findNext' ) ;
313- }
314-
315316 showFind ( ) {
316317 this . _cm . execCommand ( 'findPersistent' ) ;
317318 }
@@ -320,31 +321,33 @@ class Editor extends React.Component {
320321 this . _cm . execCommand ( 'replace' ) ;
321322 }
322323
324+ prettierFormatWithCursor ( parser , plugins ) {
325+ try {
326+ const { formatted, cursorOffset } = prettier . formatWithCursor (
327+ this . _cm . doc . getValue ( ) ,
328+ {
329+ cursorOffset : this . _cm . doc . indexFromPos ( this . _cm . doc . getCursor ( ) ) ,
330+ parser,
331+ plugins
332+ }
333+ ) ;
334+ this . _cm . doc . setValue ( formatted ) ;
335+ this . _cm . focus ( ) ;
336+ this . _cm . doc . setCursor ( this . _cm . doc . posFromIndex ( cursorOffset ) ) ;
337+ } catch ( error ) {
338+ console . error ( error ) ;
339+ }
340+ }
341+
323342 tidyCode ( ) {
324- const beautifyOptions = {
325- indent_size : INDENTATION_AMOUNT ,
326- indent_with_tabs : IS_TAB_INDENT
327- } ;
328343 const mode = this . _cm . getOption ( 'mode' ) ;
329- const currentPosition = this . _cm . doc . getCursor ( ) ;
330344 if ( mode === 'javascript' ) {
331- this . _cm . doc . setValue (
332- beautifyJS ( this . _cm . doc . getValue ( ) , beautifyOptions )
333- ) ;
345+ this . prettierFormatWithCursor ( 'babel' , [ babelParser ] ) ;
334346 } else if ( mode === 'css' ) {
335- this . _cm . doc . setValue (
336- beautifyCSS ( this . _cm . doc . getValue ( ) , beautifyOptions )
337- ) ;
347+ this . prettierFormatWithCursor ( 'css' , [ cssParser ] ) ;
338348 } else if ( mode === 'htmlmixed' ) {
339- this . _cm . doc . setValue (
340- beautifyHTML ( this . _cm . doc . getValue ( ) , beautifyOptions )
341- ) ;
349+ this . prettierFormatWithCursor ( 'html' , [ htmlParser ] ) ;
342350 }
343- this . _cm . focus ( ) ;
344- this . _cm . doc . setCursor ( {
345- line : currentPosition . line ,
346- ch : currentPosition . ch + INDENTATION_AMOUNT
347- } ) ;
348351 }
349352
350353 initializeDocuments ( files ) {
0 commit comments