freeride-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[FR-devel] Folding and Smart Indent...


From: Baptiste Lepilleur
Subject: [FR-devel] Folding and Smart Indent...
Date: Fri, 3 May 2002 23:17:03 +0200

    I was thinking about those two topics and I came to the realization that
they likely have the same solution. I'll propose an idea on how to solve
those problem. Notes that there might be a strong bias here since it is
based on the work I did for Practical Ruby's smart indent.

    Folding, for those of you who do not know provides an overview of the
code buy making some line invisible. It decompose a source in a hiearchy,
which nodes can be folded. For example:

class Editor                            // folding point level 1
    def initialize( window )        // folding point level 2
        @window = window
    end
                                            // end folding point level 2
    def title=( title )                   // folding point level 2
        @window.title = title
    end                                    // end folding point level 2
end                                        // end folding point level 1

    Typically, the user would have short cut to fold/unfold everthing at the
current level. For example, if the level 2 is folded, the user would see:
class Editor
    def initialize( window )
    def title=( title )
end

    This allows quick navigation within the source, and provides and
overview of the class methods. Folding point can be any block of code:
class, module, def, if, else, for, while...

    To be able to use folding, you must define the structure of the code:
for each line, you must indicate if it a fold point, and its level. Where it
get interesting, is that it is we need something very close to do the smart
indentation: when the user enter a new line, we should be able to say if:
    1) the line should be indented in comparison to the previous line
    2) the line should be unindented in comparison to the previous line
    3) the line should have the same indentation as the previous line

    When we analyse the source to create the folding structure, for each
line, we need to know if:
    a) it begins a new level    (class, def, if, for, while...)
    b) it ends a level    (end)
    c) it ends a level and begins a new level (else, elsif)
    d) it continue the current level (everything else)

    The correlation between both is obvious:
    - if b) or c) then the line should be unindented, unless there was
nothing between the begining of the level and the end (another way to see it
is that the indentation of the line should be set to the indentation of the
line that begun the level)
    - if a) or c) then the following lines ( typicaly of type d) ) should be
indented
    - if d) then the indentation should be identical to that of the previous
line, unless the previous line was of
      type a or c).

    I have a basic implementation of this in Practical Ruby, though I set
strong limitation (to keep thing simple to begin with): I do not handle
lines like:
if condition then if something then x = y end
x = z
else
end). It is as simple as this:

# indicates that the line should be unindented if it contains either
# 'end' or 'else' and no other key words.
def shouldUnindentLine( lineNumber )
    firstKeyWord = firstWordThatIsKeyWordAndNoEndOnLine( lineNumber )
    %w( end else ).include?( firstKeyWord )
end

Where firstWordThatIsKeyWordAndNoEndOnLine() is a method that returns the
first word with the key word style, or an empty string if the key word 'end'
is on the line and is not the first key word (the limitation I talked
about). Basicly, it tests for case b) and c)

# indicates that the line should be indented if the previous line starts
with
# 'class, def, for...' and does not contains 'end'
def shouldIndentLine( lineNumber )
    previousNonBlankLine = findNonBlankLineBefore( lineNumber )
    firstKeyWord =
irstWordThatIsKeyWordAndNoEndOnLine( previousNonBlankLine )
    %w( module class def for while do if else ).include?( firstKeyWord )
end

Where firstNonBlankLineBefore() returns the line number of the first line
that is not blank (it should also skip comment). It tests if the previous
'meaningful' line is of case a) or c), in which case, the line should be
indented.

If none of those two methods returns true, then we are in case d): same
indentation.

I think it should be possible to extend that technic to implement a
'perfect' smart indent. It would need to take in account code block ( do
|it| end), as well as ';' within a line. It is probably easier to do by
implementing method to guess if a block of code is of type a,b,c or d rather
than if it need to be indent/unindented.

One of the interesting side effect of smart indent, is that you get a source
beautifier for free: apply the smart indent on each line!

Smart indent is also a requirement for automatic refactoring (and likely
insertion of code template): you need to reindent the refactored code !

Does anyone have suggestions, or other ideas ?

Baptiste.
---
Baptiste Lepilleur <address@hidden>
http://gaiacrtn.free.fr/




reply via email to

[Prev in Thread] Current Thread [Next in Thread]