React-ace: Como usar o modo personalizado

Criado em 6 jul. 2016  ·  19Comentários  ·  Fonte: securingsincity/react-ace

Oi,

Minha pergunta pode ser um pouco estúpida (desculpas por isso), mas não consigo descobrir como usar um modo personalizado / realçador de sintaxe / preenchimento automático. Alguém pode me ajudar? Eu sou muito novo em brace, ace, e react, mas vi alguns materiais sobre como criar e definir seu modo personalizado usando o Ace. O que não consegui descobrir é como fazer o mesmo com o react-ace.

cristão

Comentários muito úteis

O código a seguir funcionou para mim:

App.js

import React, { Component } from 'react';
import brace from 'brace';
import AceEditor from 'react-ace';
import CustomSqlMode from './CustomSqlMode.js'

import 'brace/theme/github';

class App extends Component {
  componentDidMount() {
    const customMode = new CustomSqlMode();
    this.refs.aceEditor.editor.getSession().setMode(customMode);
  }

  render() {
    return (
      <div className="App">
        <AceEditor
          ref="aceEditor"
          mode="text"
          theme="github"
          name="UNIQUE_ID_OF_DIV"
          editorProps={{ $blockScrolling: true }}
        />
      </div>
    );
  }
}

export default App;

CustomSqlMode.js

import 'brace/mode/java';

export class CustomHighlightRules extends window.ace.acequire("ace/mode/text_highlight_rules").TextHighlightRules {
    constructor() {
        super();
        this.$rules = {
            "start": [{
                token: "comment",
                regex: "#.*$"
            }, {
                token: "string",
                regex: '".*?"'
            }]
        };
    }
}

export default class CustomSqlMode extends window.ace.acequire('ace/mode/java').Mode {
    constructor() {
        super();
        this.HighlightRules = CustomHighlightRules;
    }
}

O código anterior destaca apenas os comentários e strings. Não tenho certeza se o que fiz foi uma boa prática ou não, mas pelo menos funciona.

Todos 19 comentários

Oi,

e qual é a sua implementação? Posso mostrar o que funciona para mim. Não me lembro exatamente, mas acho que tive que instalar também a biblioteca brace primeiro. Então

npm install -D brace react-ace

E este é meu componente com o editor Ace:

import React from 'react'
import brace from 'brace'
import AceEditor from 'react-ace'

import 'brace/mode/javascript'
import 'brace/theme/tomorrow'

const MyEditor = () => {
  return (
    <AceEditor
      name="my-editor"
      mode="javascript"
      theme="tomorrow"
      value=""
      width="100%"
      height="500px" />
  )
}

export default MyEditor

E aqui está a captura de tela:
screen shot 2016-07-19 at 18 23 14

Eu poderia usar os modos padrão da chave, de fato. O que me pergunto é como usar meu próprio modo? Eu escrevi um modo personalizado sozinho e quero incluí-lo, mas não fui capaz de fazer isso com o react-ace até agora.

@sichvoge Você fez algum progresso nisso?

Na verdade. No final, desenvolvi meu próprio componente de reação usando Ace e não brace.

É justo - talvez você possa compartilhá-lo quando estiver satisfeito com ele? Obrigado @sichvoge

Claro, eu vou.

@sichvoge , você pode compartilhar seu componente?
Estou tendo o mesmo problema e será muito útil :)

Estou tentando fazer algo semelhante. Importar o modo no front end não parece funcionar para mim.
Parece que o AceEditor está procurando por um arquivo do lado do servidor. Então, acabei simplesmente servindo o arquivo de modo separadamente para meu script empacotado do webpack. (Esta provavelmente não é a maneira certa de fazer isso, mas parece funcionar ....)

Digamos que meu modo personalizado se chame 'eikelang'

Meu código React se parece com isto

import React from 'react';
import AceEditor from 'react-ace';
import 'brace/theme/github';

export default ({value}) => {
<AceEditor
    mode="eikelang"
    theme="github"
    width="auto"
    height="200px"
    value={value}
  />
};

Eu tenho um arquivo js chamado 'mode-eikelang.js' servido a partir do diretório raiz do meu servidor, que é apenas uma cópia do arquivo do modo mysql brace com alguns nomes alterados:

ace.define('ace/mode/doc_comment_highlight_rules',
  ['require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/text_highlight_rules'],
  function(acequire, exports, module) {
    'use strict';

    let oop = acequire('../lib/oop');
    let TextHighlightRules = acequire('./text_highlight_rules').TextHighlightRules;

    var DocCommentHighlightRules = function() {
      this.$rules = {
        'start': [{
          token: 'comment.doc.tag',
          regex: '@[\\w\\d_]+' // TODO: fix email addresses
        },
          DocCommentHighlightRules.getTagRule(),
        {
          defaultToken: 'comment.doc',
          caseInsensitive: true
        }]
      };
    };

    oop.inherits(DocCommentHighlightRules, TextHighlightRules);

    DocCommentHighlightRules.getTagRule = function(start) {
      return {
        token: 'comment.doc.tag.storage.type',
        regex: '\\b(?:TODO|FIXME|XXX|HACK)\\b'
      };
    };

    DocCommentHighlightRules.getStartRule = function(start) {
      return {
        token: 'comment.doc', // doc comment
        regex: '\\/\\*(?=\\*)',
        next: start
      };
    };

    DocCommentHighlightRules.getEndRule = function(start) {
      return {
        token: 'comment.doc', // closing comment
        regex: '\\*\\/',
        next: start
      };
    };


    exports.DocCommentHighlightRules = DocCommentHighlightRules;
  });

ace.define('ace/mode/eikelang_highlight_rules', ['require', 'exports', 'module', 'ace/lib/oop', 'ace/lib/lang', 'ace/mode/doc_comment_highlight_rules', 'ace/mode/text_highlight_rules'], function(acequire, exports, module) {
  let oop = acequire('../lib/oop');
  let lang = acequire('../lib/lang');
  let DocCommentHighlightRules = acequire('./doc_comment_highlight_rules').DocCommentHighlightRules;
  let TextHighlightRules = acequire('./text_highlight_rules').TextHighlightRules;

  let EikelangHighlightRules = function() {
    let builtins = 'drop|hash|keep|linear_transformation|map|rename|string_manipulation|string_submatcher';

    let keywordMapper = this.createKeywordMapper({
      'support.function': builtins
    }, 'identifier', true);


    function string(rule) {
      let start = rule.start;
      let escapeSeq = rule.escape;
      return {
        token: 'string.start',
        regex: start,
        next: [
                {token: 'constant.language.escape', regex: escapeSeq},
                {token: 'string.end', next: 'start', regex: start},
                {defaultToken: 'string'}
        ]
      };
    }

    this.$rules = {
      'start': [{
        token: 'comment', regex: '(?:-- |#).*$'
      },
        string({start: '"', escape: /\\[0'"bnrtZ\\%_]?/}),
        string({start: '\'', escape: /\\[0'"bnrtZ\\%_]?/}),
        DocCommentHighlightRules.getStartRule('doc-start'),
      {
        token: 'comment', // multi line comment
        regex: /\/\*/,
        next: 'comment'
      }, {
        token: 'constant.numeric', // hex
        regex: /0[xX][0-9a-fA-F]+|[xX]'[0-9a-fA-F]+'|0[bB][01]+|[bB]'[01]+'/
      }, {
        token: 'constant.numeric', // float
        regex: '[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b'
      }, {
        token: keywordMapper,
        regex: '[a-zA-Z_$][a-zA-Z0-9_$]*\\b'
      }, {
        token: 'constant.class',
        regex: '@@?[a-zA-Z_$][a-zA-Z0-9_$]*\\b'
      }, {
        token: 'constant.buildin',
        regex: '`[^`]*`'
      }, {
        token: 'keyword.operator',
        regex: '\\+|\\-|\\/|\\/\\/|%|<@>|@>|<@|&|\\^|~|<|>|<=|=>|==|!=|<>|='
      }, {
        token: 'paren.lparen',
        regex: '[\\(]'
      }, {
        token: 'paren.rparen',
        regex: '[\\)]'
      }, {
        token: 'text',
        regex: '\\s+'
      }],
      'comment': [
            {token: 'comment', regex: '\\*\\/', next: 'start'},
            {defaultToken: 'comment'}
      ]
    };

    this.embedRules(DocCommentHighlightRules, 'doc-', [DocCommentHighlightRules.getEndRule('start')]);
    this.normalizeRules();
  };

  oop.inherits(EikelangHighlightRules, TextHighlightRules);

  exports.EikelangHighlightRules = EikelangHighlightRules;
});

ace.define('ace/mode/eikelang', ['require', 'exports', 'module', 'ace/lib/oop', 'ace/mode/text', 'ace/mode/eikelang_highlight_rules'],
  function(acequire, exports, module) {
    let oop = acequire('../lib/oop');
    let TextMode = acequire('../mode/text').Mode;
    let EikelangHighlightRules = acequire('./eikelang_highlight_rules').EikelangHighlightRules;

    let Mode = function() {
      this.HighlightRules = EikelangHighlightRules;
      this.$behaviour = this.$defaultBehaviour;
    };
    oop.inherits(Mode, TextMode);

    (function() {
      this.lineCommentStart = ['--', '#']; // todo space
      this.blockComment = {start: '/*', end: '*/'};

      this.$id = 'ace/mode/eikelang';
    }).call(Mode.prototype);

    exports.Mode = Mode;
  });

Olá @ pollen8 ,
Se você der uma olhada no código react-ace, você verá que o componente tenta definir o modo usando seu nome

O que significa que você tem que ter certeza que seu modo já está no cache "Ace" usando ace.define e oop.inherits(Mode, TextMode) e outro código Ace nativo, que eu acho muito antigo quando você tem ES6 do seu lado.
Então, por que não escrever seu modo personalizado simplesmente como escrevemos qualquer outra classe?
Isso faria com que sua classe de modo personalizado parecesse muito melhor
(menos linhas de código + melhor sintaxe => menos manutenção)

Então eu:

  1. Criei minha classe de modo personalizado (código ES6 puro)
  2. Inicializou o componente com um nome de modo existente (como "sql")
  3. Usei a função componentDidMount e chamei session.setMode com uma instância do meu modo personalizado.

Meu modo personalizado é:

export default class CustomSqlMode extends ace.acequire('ace/mode/text').Mode {

    constructor(){
        super();
        // Your code goes here
    }
}

E meu código react-ace se parece com:

render() {
        return <div>
            <AceEditor
                ref="aceEditor"
                mode="sql"     // Default value since this props must be set.
                theme="chrome" // Default value since this props must be set.
            />
        </div>;
    }

    componentDidMount() {
        const customMode = new CustomSqlMode();
        this.refs.aceEditor.editor.getSession().setMode(customMode);
    }

A única desvantagem que ele vê é depender de ace.acequire('ace/mode/text').Mode para ser uma linha válida quando eu defino a classe de modo.
Mas é válido de qualquer maneira, pois react-ace coloca o módulo "ace" em window.ace (https://github.com/ajaxorg/ace/blob/4c7e5eb3f5d5ca9434847be51834a4e41661b852/lib/ace/worker/worker.js#L19)

@AlonBe @
Alguém tem um jsfiddle ou exemplo de trabalho para isso?

Tentei seguir as instruções que @AlonBe listou com o modo personalizado fornecido por @ pollen8 e, infelizmente, não consigo fazer com que o realce de sintaxe seja registrado corretamente ... :(

Qualquer ajuda seria apreciada.

custom_mode

Onde meu editor está sendo renderizado:

componentDidMount () { const customMode = new CustomSqlMode(); this.refs.ace.editor.getSession().setMode(test); console.log(customMode); }

@solemnify , Você tentou fazer this.refs.ace.editor.getSession().setMode(customMode); ?
Tente seguir a classe CustomSqlMode que criei.
Para ver se funciona, você pode colocar dentro do construtor a seguinte linha:
this.lineCommentStart = '--';

Se você quiser usar o realce de sintaxe, você precisa substituir this.HighlightRules da classe de modo personalizado.

Dê uma olhada em um dos modos atuais, como sql_mode

@AlonBe
Você poderia fornecer um exemplo de código para colocar em seu componente CustomSqlMode?
Não sei como transformar as regras de destaque em es6 ...
Agradeço antecipadamente por sua ajuda.

O código a seguir funcionou para mim:

App.js

import React, { Component } from 'react';
import brace from 'brace';
import AceEditor from 'react-ace';
import CustomSqlMode from './CustomSqlMode.js'

import 'brace/theme/github';

class App extends Component {
  componentDidMount() {
    const customMode = new CustomSqlMode();
    this.refs.aceEditor.editor.getSession().setMode(customMode);
  }

  render() {
    return (
      <div className="App">
        <AceEditor
          ref="aceEditor"
          mode="text"
          theme="github"
          name="UNIQUE_ID_OF_DIV"
          editorProps={{ $blockScrolling: true }}
        />
      </div>
    );
  }
}

export default App;

CustomSqlMode.js

import 'brace/mode/java';

export class CustomHighlightRules extends window.ace.acequire("ace/mode/text_highlight_rules").TextHighlightRules {
    constructor() {
        super();
        this.$rules = {
            "start": [{
                token: "comment",
                regex: "#.*$"
            }, {
                token: "string",
                regex: '".*?"'
            }]
        };
    }
}

export default class CustomSqlMode extends window.ace.acequire('ace/mode/java').Mode {
    constructor() {
        super();
        this.HighlightRules = CustomHighlightRules;
    }
}

O código anterior destaca apenas os comentários e strings. Não tenho certeza se o que fiz foi uma boa prática ou não, mas pelo menos funciona.

@ newint33h obrigado! Funciona para mim.

@ newint33h Tentei usar essa estrutura, mas ela reclama que TypeError: Não é possível ler a propriedade 'editor' de undefined.

@shuotongli Eu tive o mesmo problema e parece que o problema é porque o this.refs do React agora está obsoleto. Existem algumas maneiras de criar um ref, dependendo da versão do React que você está usando. Para a forma atualmente suportada, verifique isto:
https://reactjs.org/docs/refs-and-the-dom.html#creating -refs

O código a seguir funcionou para mim:

App.js

import React, { Component } from 'react';
import brace from 'brace';
import AceEditor from 'react-ace';
import CustomSqlMode from './CustomSqlMode.js'

import 'brace/theme/github';

class App extends Component {
  componentDidMount() {
    const customMode = new CustomSqlMode();
    this.refs.aceEditor.editor.getSession().setMode(customMode);
  }

  render() {
    return (
      <div className="App">
        <AceEditor
          ref="aceEditor"
          mode="text"
          theme="github"
          name="UNIQUE_ID_OF_DIV"
          editorProps={{ $blockScrolling: true }}
        />
      </div>
    );
  }
}

export default App;

CustomSqlMode.js

import 'brace/mode/java';

export class CustomHighlightRules extends window.ace.acequire("ace/mode/text_highlight_rules").TextHighlightRules {
  constructor() {
      super();
      this.$rules = {
          "start": [{
              token: "comment",
              regex: "#.*$"
          }, {
              token: "string",
              regex: '".*?"'
          }]
      };
  }
}

export default class CustomSqlMode extends window.ace.acequire('ace/mode/java').Mode {
  constructor() {
      super();
      this.HighlightRules = CustomHighlightRules;
  }
}

O código anterior destaca apenas os comentários e strings. Não tenho certeza se o que fiz foi uma boa prática ou não, mas pelo menos funciona.

porque o modo = 'texto', o que significa texto?

@TaurusWood , como o primeiro render ocorre antes de componentDidMount - você deve passar um "modo" válido para o componente react-ace.
mais tarde, em componentDidMount você pode ver que o código substitui esse modo ( setMode )

e BTW, se eu não estou entendendo errado, mode = 'text' é o modo básico do Ace que todos os outros estão substituindo

Alguém pode explicar por que não está mais funcionando, todos os meus códigos são interpretados como um texto em vez de um arquivo java depois de definir um modo personalizado? Obrigado

O código a seguir funcionou para mim:

App.js

import React, { Component } from 'react';
import brace from 'brace';
import AceEditor from 'react-ace';
import CustomSqlMode from './CustomSqlMode.js'

import 'brace/theme/github';

class App extends Component {
  componentDidMount() {
    const customMode = new CustomSqlMode();
    this.refs.aceEditor.editor.getSession().setMode(customMode);
  }

  render() {
    return (
      <div className="App">
        <AceEditor
          ref="aceEditor"
          mode="text"
          theme="github"
          name="UNIQUE_ID_OF_DIV"
          editorProps={{ $blockScrolling: true }}
        />
      </div>
    );
  }
}

export default App;

CustomSqlMode.js

import 'brace/mode/java';

export class CustomHighlightRules extends window.ace.acequire("ace/mode/text_highlight_rules").TextHighlightRules {
  constructor() {
      super();
      this.$rules = {
          "start": [{
              token: "comment",
              regex: "#.*$"
          }, {
              token: "string",
              regex: '".*?"'
          }]
      };
  }
}

export default class CustomSqlMode extends window.ace.acequire('ace/mode/java').Mode {
  constructor() {
      super();
      this.HighlightRules = CustomHighlightRules;
  }
}

O código anterior destaca apenas os comentários e strings. Não tenho certeza se o que fiz foi uma boa prática ou não, mas pelo menos funciona.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

nenadlukic picture nenadlukic  ·  6Comentários

Jarmahent picture Jarmahent  ·  3Comentários

kolbinski picture kolbinski  ·  5Comentários

henviso picture henviso  ·  7Comentários

ponelat picture ponelat  ·  3Comentários