Skip to content

Ace Mode

Zijian Zhou edited this page Apr 2, 2020 · 4 revisions
  • The previous editor mode was adapted from the built-in javascript mode from ace-builds. Previously, js-slang made use of the default javascript mode from the ace-builds library. However, as Source Academy is growing in size, there is an urgent need to customize the editor mode to better suit the need of the Source Academy.

  • Currently, we have developed our customized ace editor designed specially for source. We shall take a closer look at the benefits brought by the customized ace editor in the following page.

Source Mode Features

  • Overview

    1. The customized source mode is stored inside js-slang as an api for cadet-front to use:

      • To include source mode, simply import it from js-slang as follows.
      import { HighlightRulesSelector, ModeSelector } from 'js-slang/dist/editors/ace/modes/source';
      import 'js-slang/dist/editors/ace/theme/source';
    2. Separation of Source Chapters:

      • Previously, by importing javascript mode from ace-builds as a bulk, we lack flexibility to adjust for the difference between different source chapters. Now, with source mode, we can specify the special features we want to include for different chapters.

        export function HighlightRulesSelector(id: number) {
          // @ts-ignore
          function _SourceHighlightRules(acequire, exports, module) {

        As shown in the above code from source.ts inside js-slang, the function HighlightRulesSelector requires id as an argument. As such we can manipulate the function by including the chapter id and the features to be developed. This makes further extension of the source mode much more convenient.

    3. Composition:

      • The current source mode is composed of two parts: The _SourceHighlightRules and the _Mode. The _SourceHighlighRules involves codes relating to the categorisation of tokens and the parsing method, while the _Mode takes the _SourceHighlightRules and integrate it with Javascript Mode to ensure that source functions properly as a subset of Javascript language.
  • Features

    1. Syntax highlighting Respect to Source Chapters.

      • With the benefit provided by Source Mode, we can specified as set of functions, keywords and forbidden words inside source.ts and asks the _SourceHighlightRules to handle them for us.

        const chapter1 = {
              keywords: 'const|else|if|return|function',
              functions:
                'display|error|is_boolean|is_function|is_number|is_string|is_undefined|' +
                'math_abs|math_acos|math_acosh|math_asin|math_asinh|math_atan|' +
                'math_atan2|math_atanh|math_cbrt|math_ceil|math_clz32|' +
                'math_cos|math_cosh|math_exp|math_expm1|math_floor|math_fround|math_hypot|math_imul|' +
                'math_log|math_log1p|math_log2|math_log10|math_max|math_min|math_pow|math_random|' +
                'math_round|math_sign|math_sin|math_sinh|math_sqrt|math_tan|math_tanh|' +
                'math_tanh|math_trunc|parse_int|prompt|runtime|stringify'
            }
        
            const chapter2 = {
              keywords: '',
              functions:
                'accumulate|append|build_list|' +
                'draw_data|enum_list|equal|error|filter|for_each|head|' +
                'is_pair|length|list|list_ref|list_to_string|' +
                'map|member|pair|parse_int|prompt|remove|remove_all|reverse|runtime|tail'
            }
        
            const chapter3 = {
              keywords: 'while|for|break|continue|let',
              functions:
                'array_length|build_stream|enum_stream|' +
                'eval_stream|integers_from|is_array|is_stream|' +
                'list_to_stream|set_head|set_tail|stream|stream_append|' +
                'stream_filter|stream_for_each|stream_length|' +
                'stream_map|stream_member|stream_ref|stream_remove|' +
                'stream_remove_all|stream_reverse|stream_tail|stream_to_list'
            }
        
            const chapter34 = {
              keywords: '',
              functions: 'test_and_set|clear|concurrent_execute'
            }
        
            const chapter4 = {
              keywords: '',
              functions: 'apply_in_underlying_javascript'
            }

        As shown in the above code, we have specified the functions and keywords to be used in different chapters as of Apr 2nd, 2020. We can easily edit this set in the future as source language progresses.

      • The source mode internally categorizes tokens into 5 different types: support.function, constant.language, variable.language, storage.type, and keyword. Inside the editor, the tokens will be highlighted accordingly in different colours. Most importantly, this will respect the source chapter id. As such, we can define functions or keywords that are forbidden in certain chapters.

      • Moreover, we can also highlight regex expressions. In the current version of source mode, we have already made an effort to highlight all expressions such as +=, ++, -- in pink, the forbidden colour. The related code is shown below:

        {
           token: ['variable.language'],
           regex: /\.{3}|--+|\+\++|\^|(==|!=)[^=]|[$%&*+\-~\/^]=+|[^&]*&[^&]|[^\|]*\|[^\|]/
        },
        {
           token: 'keyword.operator',
           regex: /===|=|!==|<+=?|>+=?|!|&&|\|\||[%*+-\/]/,
           next: 'start'
        },   
                    
    2. Supporting Editor

      • The source mode features discussed above allows more features to be built on. For example, the customized categorisation of tokens makes loop-up features in the cadet-front editor an easy job. Since we have defined the set of source functions, we can just ask AceEditor to check if a token is of the type support.function and perform loop-ups. The code is shown below:

          {
            name: 'navigate',
              bindKey: {
                win: 'Ctrl-B',
                  mac: 'Command-B'
              },
                exec: this.handleNavigate
          },
          
          
          private handleNavigate = () => {
            const chapter = this.props.sourceChapter;
            const pos = (this.AceEditor.current as any).editor.selection.getCursor();
            const token = (this.AceEditor.current as any).editor.session.getTokenAt(pos.row, pos.column);
            const url = LINKS.TEXTBOOK;
            if (token !== null && /\bsupport.function\b/.test(token.type)) {
              window.open(`${url}/source/source_${chapter}/global.html#${token.value}`); // opens the link
            } else if (token !== null && /\bstorage.type\b/.test(token.type)) {
              window.open(`${url}/source/source_${chapter}.pdf`);
            }
          };

        As demonstrated in the code, we used /\bsupport.function\b/.test(token.type) to test if a token is of the type support.function. Then we can perform the look-up.

Future Development

  1. Currently, the Ace editor only provides source mode. In the future, we may include modes for other languages.
  2. Improve the mode to provide support for a smarter editor. The mode can be further improved to include features that make the editor more user friendly.

Authors

Zhou Zijian...
Hopefully more join in