codemap.vscode icon indicating copy to clipboard operation
codemap.vscode copied to clipboard

Improving Generic Map by Reworking levelIndent

Open omemisoglu opened this issue 1 year ago • 5 comments

Description

Implementing hierarchy level by promoting levelIndent to identify not a whitespace indent on files, but a hierarchy level among other mapping rules of a indent dependant programming language file.

Non-hiearachy Mapping Rules

  • If a mapping rule has no hiearchy, it cannot nest other mapping rules.

Hierarchy Priority

  • Mapping rules with levelIndent property set other than zero (0) can nest lower level hierarchies (lower number, higher hierarchy).

Resolving Hierarchy with same Indentation

  • When mapping rules with levelIndent has same indentation, higher hierarchy mapping rule will nest lower level hierarchy.

TODO

  • Implementing mapping rule category: When different categorie mapping rules with same indentation but different hierarchy levels can nest each other.
  • Supporting non-indent based programming languages by improving hierarchy algorithm.

omemisoglu avatar Aug 26 '23 11:08 omemisoglu

Python Generic Mapper Example for Testing

"codemap.py": [
    // Cell Pattern
    {
        "pattern": "^ *#%% .*$",
        "clear": " *#%% *(?=.*$)",
        "prefix": "# ",
        "icon": "level1",
        "levelIndent": 1
    },
    // Class
    {
        "pattern": "^ *class \\w+(?=.*$)",
        "clear": "(([^#\\r\\n]*)class )(?=\\w+)",
        "prefix": "",
        "icon": "class",
        "levelIndent": 2
    },
    // Functions
    {
        "pattern": " *def \\w+(?=\\(.*\\).*)",
        "clear": " *def ",
        "prefix": "",
        "icon": "function",
        "levelIndent": 2
    },
    // Section Comment (Spyder)
    {
        "pattern": "^ *#### .*",
        "clear": " *#### *",
        "prefix": "- ",
        "icon": "level2"
    },
    // CONSTANTS
    {
        "pattern": "^ *[A-Z]+[A-Z0-9_]*(?= [=|:].*$)",
        "clear": "^ *",
        "prefix": "",
        "icon": "interface"
    }
]

omemisoglu avatar Aug 26 '23 11:08 omemisoglu

I @omemisoglu can you please describe the problem you are trying to solve? It's not entirely clear to me.

We might be asking the regular expression technique too much to do.

Can you describe the scenario that exhibits the undesirable behaviour or fails?

oleg-shilo avatar Aug 27 '23 07:08 oleg-shilo

@oleg-shilo Thank yo for your reply. I want to have mapping elements in Python that can nest some chunks of a source code. However, when I start to use levelIndent properties by assigning levels to all of the mappings or some of the mappings, the codemap tree is not behaving the way I want. There are 7 mappings that I want to see outlined:

  • Cell Comment (#%%): When encountering higher-level or no-level hierarchy maps, they cannot be nested, hence they will never be indented.

  • Section Comment (####): Section comments can be nested only by Cell Comment on the same indent level, but it cannot nest functions and classes on the same indent level. They can be nested when they are more indented than functions and classes.

  • Functions(def func()) and Classes(class()): Functions and classes share the same level with section comments. The same rules apply.

  • Imports, Constants and Marking Comments(##): These categories must be encounterable on the tree view, however, they cannot nest other elements that are more indented than themselves.

When applying the mapping below on the source code attached, the attached tree map is generated and the problems below occurred:

  1. IMPORTS and FUNCTIONS are Cell Comments which should not nest within each other, hence they are never indented.
  2. Imports should never nest

Codemap Example 01

codemap_01

// Cell Pattern
"codemap.py": [
        {
            "pattern": "^ *#%% .*$",
            "clear": " *#%% *(?=.*$)",
            "prefix": "# ",
            "icon": "level1",
            "levelIndent": 4
        },
        // Class
        {
            // "pattern": "^( *class )\\w+(?=.*$)",
            "pattern": "^ *class \\w+(?=.*$)",
            // "clear": "(^(([^#\\r\\n]*)class )(?=\\w+))|(\\(.*$)",
            "clear": " *class ",
            // "clear": "(([^#\\r\\n]*)class )(?=\\w+)",
            // "clear": "class|:|\\)|\\(",
            // "clear": "^(.*)class (?=\\w+))|(\\(.*$)",
            "prefix": "",
            "icon": "class",
            "levelIndent": 8
        },
        // Functions
        {
            "pattern": " *def \\w+(?=.*$)",
            // "pattern": "^( *)def (.*)$",
            "clear": " *def ",
            // "clear": "(([^#\\r\\n]*)def )(?=\\w+)",
            // "clear": "(^(([^#\\r\\n]*)def )(?=\\w+))|(\\(.*$)",
            // "clear": "^(.*)def (?=\\w+))|(\\(.*$)",
            // "clear": "def|:|\\)|\\(",
            "prefix": "",
            "icon": "function",
            "levelIndent": 8
        },
        // Section Comment (Spyder)
        {
            // "pattern": "^(.*)#### ([^a-z\\r\\n])(.*)",
            "pattern": "^ *#### .*",
            "clear": " *#### *",
            "prefix": "- ",
            "icon": "level2",
            "levelIndent": 4
        },
        // Marker Comment Custom
        {
            // "pattern": "^(.*)#### ([^a-z\\r\\n])(.*)",
            "pattern": "^ *## .*",
            "clear": " *## *",
            "prefix": "",
            "icon": "level3",
            "levelIndent": 12
        },
        // CONSTANTS
        {
            "pattern": "^ *[A-Z]+[A-Z0-9_]*(?= [=|:].*$)",
            "clear": "^ *",
            "prefix": "",
            "icon": "interface",
            "levelIndent": 12
        },
        // Imports
        {
            "pattern": "^((import [\\w\\. ]+)|(from [\\w\\. ]+ import [\\w\\. ]+))",
            "clear": "^ *",
            "prefix": "",
            "icon": "property",
            "levelIndent": 8
        }
],

omemisoglu avatar Aug 27 '23 10:08 omemisoglu

Then I feel that you will have a way more manageable solution if you just implement your custom mapper in JS as a dedicated mapper.

Here is the sample: https://github.com/oleg-shilo/codemap.vscode/wiki/Adding-custom-mappers#mapping-with-a-dedicated-mapper

In fact, that was the idea behind custom mappers - implementing them in high-level languages and regex to be used only for trivial cases.

Instead of a declarative JSON section, you can have an Imperative code (JS file) and simply specify it in the user settings.

You can have your algorithm as complicated as you want. And you can even fall back to the use of regex :)

You will have ultimate flexibility and will not be bound by the contents of a generic mapper (e.g. processing ALL lines of the code) you are trying to use currently.

After all the only thing that you need to produce is a simple code tree specification (map definition) like this one:

def settings()|1|function
class NavigateCodeMap|4|class
    def highlight_line()|6|function
    def keep_going_down()|9|function
        def indented()|11|function
    def keep_going_up()|16|function
        def indented()|18|function
        def up()|23|function
def down()|26|function

oleg-shilo avatar Aug 27 '23 11:08 oleg-shilo

And a mapping specification/definition is a collection of strings/lines where every line describes a code map item via a very simple format:

[indent]<name>|<line>|<icon>

oleg-shilo avatar Aug 27 '23 11:08 oleg-shilo