Author Topic: Julia Scope Delimiters  (Read 163 times)

RaffoPazzo

  • Community Member
  • Posts: 70
  • Hero Points: 2
Julia Scope Delimiters
« on: April 19, 2021, 07:43:43 am »
Hi guys, I know you are planning to add built-in support for Julia some time soon. In the meantime I'm adding it as a user defined language. So far so good but there is something about SlickEdit that I have never understood how to do (if at all possible): how to tell it what delimits a scope. I use vim bindings, so when coding in C++ pressing % takes me to the matching brace which is nice and sort-of expected. In CMake it somehow seem to know that it should take me from if() <--> endif() and back, which is super cool. I would like to do the same in for let/end - begin/end - function/end. It might be challenging for Julia since the meaning of 'end' depends on the scope itself but, in general, how can I configure the scope delimiters for a custom language? Perhaps it's not done via color-coding settings but via Slick-C? If so, how?

Clark

  • SlickEdit Team Member
  • Senior Community Member
  • *
  • Posts: 5735
  • Hero Points: 458
Re: Julia Scope Delimiters
« Reply #1 on: April 19, 2021, 02:01:45 pm »
You might want to wait for v26 beta to come out (about ~3.5 months out). It's quite a bit of work to add support for a language. Color coding can be easy but it's difficult to determine the identifier start and follow characters for Julia. In fact, we added a new regex tweak to assist with the start and follow regex. We also added support for using 'f' in a floating point exponent. Although, you can write a regex to mostly work around that.

Here's the Slick-C begin/end matching code we're currently using for Julia in v26 (not even in beta yet). Not trivial. This code assumes that "julia" is the language id which needs to be used for all Julia callbacks. Also, it assumes that color coding keywords have been defined for all the begin/end pairs. When I added "Julia" as a new language to 25.0.1, it chose "julia" as the language id. I can't promise that the code below will compile in v25. I posted it in case you wanted to try to dive in.

Code: [Select]
#pragma option(pedantic,on)
#region Imports
#include "slick.sh"
#include "project.sh"
#import "se/lang/api/LanguageSettings.e"
#import "se/ui/AutoBracketMarker.e"
#import "cutil.e"
#import "c.e"
#import "stdcmds.e"
#import "groovy.e"
#import "adaptiveformatting.e"
#import "alias.e"
#import "autocomplete.e"
#import "beautifier.e"
#import "cfcthelp.e"
#import "cjava.e"
#import "csymbols.e"
#import "diffprog.e"
#import "env.e"
#import "gradle.e"
#import "hotspots.e"
#import "java.e"
#import "javacompilergui.e"
#import "main.e"
#import "notifications.e"
#import "optionsxml.e"
#import "picture.e"
#import "pmatch.e"
#import "project.e"
#import "projconv.e"
#import "refactor.e"
#import "scala.e"
#import "slickc.e"
#import "smartp.e"
#import "sbt.e"
#import "stdprocs.e"
#import "tags.e"
#import "vc.e"
#import "wkspace.e"
#import "surround.e"
#endregion

using se.lang.api.LanguageSettings;
using se.ui.AutoBracketMarker;


int _julia_find_matching_word(bool quiet,
                          int pmatch_max_diff_ksize=MAXINT,
                          int pmatch_max_level=MAXINT)
{
/*
   if
   elseif
   else
   end

   for
   end

   while
   end

   begin
   end

   try
   catch
   finally
   end

   module
   end

   basemodule
   end

   abstract type
   end

   primative type
   end


   mutable struct
   end

   struct
   end

   quote
   end

   function
   end

   macro
   end

   let
   end

*/
   
   cfg:=_clex_find(0,'G');
   if (cfg!=CFG_KEYWORD) {
      return 1;
   }
   _str word=cur_identifier(auto start_col);

   save_pos(auto p);
   if (word=='type') {
      p_col= start_col;
      if (p_col==1) {
         if (!quiet) {
            message(nls('Not on begin/end or paren pair'));
         }
         return 1;
      }
      orig_line:=p_line;
      left();_clex_skip_blanks('h-');
      word2:=cur_identifier(auto start_col2);
      if (orig_line!=p_line || (word2!='abstract' && word2!='primative')) {
         restore_pos(p);
         if (!quiet) {
            message(nls('Not on begin/end or paren pair'));
         }
         return 1;
      }
   } else if (word=='abstract' || word=='primative' || word=='mutable') {
      p_col=start_col+length(word);
      orig_line:=p_line;
      _clex_skip_blanks('h');
      word2:=cur_identifier(auto start_col2);
      if (orig_line!=p_line ||
          (word!='mutable' && (word2!='type')) ||
          (word=='mutable' && (word2!='struct'))
         ) {
         restore_pos(p);
         if (!quiet) {
            message(nls('Not on begin/end or paren pair'));
         }
         return 1;
      }
      word=word2;
      start_col=start_col2;
   } else if (!pos(' 'word' ',' end let function macro if else elseif for while begin try catch finally module basemodule struct quote ')) {
      if (!quiet) {
         message(nls('Not on begin/end or paren pair'));
      }
      return 1;
   }
   bool backwards=false;
   _str options='@wrhck';
   if (word=='end') {
      options='-':+options;
      backwards=true;
   }
   re:='let|function|macro|if|for|while|begin|try|module|basemodule|struct|quote|end|abstract[ \t]#type|primative[ \t]#type';
   if (!backwards) {
      re='elseif|else|catch|finally|':+re;
   }
   if (backwards) {
      if (start_col<=1) {
         up();_end_line();
      } else {
         p_col=start_col-1;
      }
   } else {
      p_col=start_col+length(word);
   }
   int nest_level=1;
   status:=search(re,options);
   for (;;) {
      if (status) {
         break;
      }
      word=cur_identifier(start_col);
      if (word=='end') {
         if (backwards) {
            ++nest_level;
         } else {
            --nest_level;
            if (nest_level<1) {
               p_col=start_col;
               return 0;
            }
         }
      } else if (!backwards && pos(' 'word' ',' else elseif catch finally ')) {
         if (nest_level==1) {
            p_col=start_col;
            return 0;
         }
      } else if (pos(' 'word' ',' let function macro if else elseif for while begin try catch finally module basemodule struct quote abstract primative ')) {
         if (backwards) {
            --nest_level;
            if (nest_level<1) {
               if (word=='struct') {
                  p_col= start_col;
                  save_pos(auto p2);
                  if (p_col>1) {
                     orig_line:=p_line;
                     left();_clex_skip_blanks('h-');
                     word1:=cur_identifier(auto start_col2);
                     if (orig_line!=p_line || (word1!='mutable')) {
                        restore_pos(p2);
                     } else {
                        start_col=start_col2;
                     }
                  }
               }
               p_col=start_col;
               return 0;
            }
         } else {
            ++nest_level;
         }
      }
      status=repeat_search();
   }
   restore_pos(p);
   return 1;
}

Dennis

  • SlickEdit Team Member
  • Senior Community Member
  • *
  • Posts: 3231
  • Hero Points: 459
Re: Julia Scope Delimiters
« Reply #2 on: April 19, 2021, 02:28:08 pm »
If you want a simple, but only partial solution, set the begin/end pairs.

Document > Julia Options... > General > Begin/End pairs:   

    (if),(for),(while),(begin),(try),(module),(basemodule),(type),(struct),(quote),(function),(macros),(let)|(end)

This expression is basically a list of start keywords that pair with "end" as an end keyword.  it ignores everything in the middle (elseif, else, catch, finally), as well as it does not handle multi-word start keywords like "mutable struct".

RaffoPazzo

  • Community Member
  • Posts: 70
  • Hero Points: 2
Re: Julia Scope Delimiters
« Reply #3 on: April 19, 2021, 02:36:02 pm »
Thank you guys! For now I applied Dennis settings and this works super slick! I think 80% of the cases with 20% of the effort is enough for now until you guys support 100% of the cases ;D

By the way I think there is a couple of mistakes in
Quote
(if),(for),(while),(begin),(try),(module),(basemodule),(type),(struct),(quote),(function),(macros),(let)|(end)
I think
  • basemodule, should be baremodule
  • macros -> macro
  • 'do' is missing, which I added in my case

Thanks a lot!

Clark

  • SlickEdit Team Member
  • Senior Community Member
  • *
  • Posts: 5735
  • Hero Points: 458
Re: Julia Scope Delimiters
« Reply #4 on: April 19, 2021, 03:11:13 pm »
Thanks. Please beta test v26 Julia support when it's available.