fvwmorg / fvwm3

FVWM version 3 -- the successor to fvwm2
Other
515 stars 78 forks source link

Ideas for a new config syntax #405

Closed lgsobalvarro closed 1 year ago

lgsobalvarro commented 3 years ago

With the aim of streamlining/modernizing FVWM3 syntax and trying to make it a bit more friendly for new users. I put forward the following ideas of how it could work.

This proposal is inspired by CSS/SASS.

Module invocation

Module {
    +I FvwmPager;
    +I FvwmButtons.RightPanel;
}

Functions

Functions {
    IconManClick {
        +I ThisWindow {
            (Raised, !Shaded, !Iconic, CurrentPage) Iconify;
            (!Raised) Raise;
            (Shaded) WinshowShade;
            (Iconic) Iconify;
            (AcceptsFocus) FlipFocus;
        }
        +I TestRc { (Match) Break; }
    }
    AnotherFunction { 
        foo;
    }
}

Styles

Styles {
    #This is applied to everything (aka global style)
    MnmButtons;
    MwmBorder;
    MwmDecor;
    SloppyFocus;
    #This affects only RightPanel
    .RightPanel {
        !Title;
        Sticky;
        WindowListSkip;
    }
    #Apply same style to multiple applications (gcolor, gedit, epiphany)
    gcolor3, gedit, epiphany {
        !MwmDecor
    }
    #Apply previous style plus an specific one for gcolor3
    gcolor3 {
        Layer 6;
    }
}

Module Instances

.RightPanel {
    ButtonGeometry 64x64-0+0;
    Colorset 10;
    Columns 1;
    Font "xft:Sans:size=9";
    1x3 { Swallow "FvwmPager" 'FvwmPager 0 3'};
    1x1 { Swallow (NoClose, UseOld) "xclock" 'Exec xclock'};
}
ThomasAdam commented 3 years ago

Hi @lgsobalvarro,

Thanks for sending this in -- it's nice to see someone else other than myself has thought about this.

What you're suggesting makes a lot of sense to me. Putting aside any analogies to a CSS or SASS structure, the idea of having configuration blocks, as in:

Style {
   ...
}

Module {
   ...
}

Isn't anything new -- it's how twm has structured its configuration file since the beginning (and fvwm being based on twm would have inherited that before moving away to the line-based syntax we have now).

I certainly don't disagree that moving to a block-based structure might be a good idea. However, I'm also thinking about a few things on top of that:

In terms of external scripting, what I'm thinking about here is bringing consistency to the commands which fvwm has -- similar to what tmux does. Indeed, I've referenced this here: (https://github.com/fvwmorg/fvwm3/blob/master/dev-docs/NEW-COMMANDS.md) which I recommend reading. The point is to:

Note that by duplication, I'm referring to styles (affecting windows), and commands which work on windows. There's a lot of overlap between the two which would help to reduce the confusion if there's a consistent means of referring to things like desktops, pages, windows, etc.

We also need to consider whether we need to write our own configuration syntax, or if an existing library would be useful instead. libconfuse seems OK, but there's more power (and hence complexity) in writing our own config syntax via flex/yacc.

I'm sure I'll have more thoughts in due course.

danespen commented 3 years ago

Thomas Adam notifications@github.com writes:

Hi @lgsobalvarro,

Thanks for sending this in -- it's nice to see someone else other than myself has thought about this.

What you're suggesting makes a lot of sense to me. Putting aside any analogies to a CSS or SASS structure, the idea of having configuration blocks, as in:

Style { ... }

Note that the current Fvwm accepts commands from multiple sources at the same time. Modules can send commands. They need to be atomic, not multi-line structures.

Back when I started using Fvwm, I came from an TWM variant that used structures. I was happy to get away from that.

-- Dan Espen

ThomasAdam commented 3 years ago

Hey @danespen,

Note that the current Fvwm accepts commands from multiple sources at the same time. Modules can send commands. They need to be atomic, not multi-line structures.

Indeed -- that's actually a really good point. It's worth remembering that configs aren't static (a point in time) but there are existing commands in fvwm which can send input to other commands. The suggestion from @lgsobalvarro doesn't cover this -- and if fvwm3 ever learned of commands via argc/argb (akin to how cvs works), this would need thinking about quite carefully in terms of how this would fit in with config file, vs sending commands to fvwm3.

@danespen -- isn't what you're referring to the crux of how FvwmAnimate works? I seem to recall that FvwmAnimate can be sent commands to do things -- most notably from FvwmIconMan but I suppose it's not limited to that.

ThomasAdam commented 3 years ago

Back when I started using Fvwm, I came from an TWM variant that used structures. I was happy to get away from that.

@danespen -- I have to ask (as I'm always curious!), aside from Fvwm itself, I wasn't aware of a direct TWM variant at the same time. Do you happen to recall what this was?

danespen commented 3 years ago

Thomas Adam notifications@github.com writes:

Back when I started using Fvwm, I came from an TWM variant that used structures. I was happy to get away from that.

@danespen -- I have to ask (as I'm always curious!), aside from Fvwm itself, I wasn't aware of a direct TWM variant at the same time. Do you happen to recall what this was?

There were a bunch. I was using cvtwm. I believe there was also a vtwm (without colored pixmap icons).

-- Dan Espen

danespen commented 3 years ago

Thomas Adam notifications@github.com writes:

Hey @danespen,

Note that the current Fvwm accepts commands from multiple sources at the same time. Modules can send commands. They need to be atomic, not multi-line structures.

Indeed -- that's actually a really good point. It's worth remembering that configs aren't static (a point in time) but there are existing commands in fvwm which can send input to other commands. The suggestion from @lgsobalvarro doesn't cover this -- and if fvwm3 ever learned of commands via argc/argb (akin to how cvs works), this would need thinking about quite carefully in terms of how this would fit in with config file, vs sending commands to fvwm3.

@danespen -- isn't what you're referring to the crux of how FvwmAnimate works? I seem to recall that FvwmAnimate can be sent commands to do things -- most notably from FvwmIconMan but I suppose it's not limited to that.

Yes I believe that was when the issue dawned on me during writing FvwmAnimate.

I forget if FvwmTalk or FvwmForm worried about the issue.

Fvwm has a couple of cases this matters:

When you use a backslash to continue a command, Fvwm could get confused if it got a backslashed command from another source. For that reason, if FvwmTalk wanted backslashes to work it would have to assemble the 2 lines before it sent them.

The other issue is the "+" command that continues functions, etc. I just avoid using the "+" command. Functions are normally defined in the base config file so it's not a serious problem.

It's best to remember, Fvwm takes commands, it doesn't read a configuration.

-- Dan Espen

lgsobalvarro commented 3 years ago

Yes. I have thought of this you mention @danespen.

In fact I was asking myself: How could this I'm proposing work for... let's say... FvwmConsole? I think there could be a way to go about this (from a non-programmer perspective):

  1. Separate commands (quit, restart, read and so on...) from styling. I mean: keep most commands as they are. After all they are straight forward enough.
  2. Being able to use an "atomic" structure, as @danespen suggests for styling in case a module needs to pass it for whatever reason. This atomic structure could be similar to the way inline css works.

Of course this is not a perfect solution. For one it wouldn't be as cleaner as having a complete block syntax config. Still it would simplify styling and probably shorten config files.

Part of my intention with this proposal is to get the conversation going. As @ThomasAdam has point out, there are duplicates (and triplicates?) of commands. This could be a good excuse to start a little cleanup on that area and somehow streamline the config file/process. I myself, find it a bit annoying having to -for instance- define a style for each gnome application I use in order to force it to show proper window decorations, when this could be defined just once and simply list the applications you want to apply this style to.

danespen commented 3 years ago

lgsobalvarro notifications@github.com writes:

Yes. I have thought of this you mention @danespen.

In fact I was asking myself: How could this I'm proposing work for... let's say... FvwmConsole? I think there could be a way to go about this (from a non-programmer perspective):

1 Separate commands (quit, restart, read and so on...) from styling. I mean: keep most commands as they are. After all they are straight forward enough. 2 Being able to use an "atomic" structure, as @danespen suggests for styling in case a module needs to pass it for whatever reason. This atomic structure could be similar to the way inline css works.

I think you're arguing that styling doesn't have to be done as commands because styling is done once during startup?

If so, I don't think I agree. A styling app would restyle Fvwm interactively. I have one and there are a few others floating around.

Of course this is not a perfect solution. For one it wouldn't be as cleaner as having a complete block syntax config. Still it would simplify styling and probably shorten config files.

Using blocks would be shorter, but I'm not sure I agree about simpler.

What's simpler here:

AddToFunc Warp I Next [$0] Iconify -1

or

AddToFunc Warp I Next [$0] Iconify -1 AddToFunc Warp I Next [$0] WarpToWindow 24p 24p AddToFunc Warp I None [$0] Beep

I think the later is simpler because no new operator is introduced.

Part of my intention with this proposal is to get the conversation going. As @ThomasAdam has point out, there are duplicates (and triplicates?) of commands. This could be a good excuse to start a little cleanup on that area and somehow streamline the config file/process. I myself, find it a bit annoying having to -for instance- define a style for each gnome application I use in order to force it to show proper window decorations, when this could be defined just once and simply list the applications you want to apply this style to.

What you proposed looked pretty neat. When I saw it the second time I realized the problem it would create.

If I was trying to improve the command syntax of Fvwm I'd be looking at how Fvwm does parsing. I'd try to make the parsing as table driven as possible. Right now Fvwm uses a lot of functions that aid in parsing, for example, GetNextToken. But those functions are joined up with logic instead of a table.

An example of a table driven parser is the one used for TSO CLISTS. The table names all the keywords and names the variables where the parsed result is put. The table fvwm could use would contain things like this keyword must be followed by a geometry or a color name. It could do things like declare that a keyword must be followed by a number and define the range.

I haven't looked deeply but I suspect that trying to parse commands with tables might force some commands to change syntax.

I've done things like this more than once and it's not easy but it does reduce code size.

-- Dan Espen

lgsobalvarro commented 3 years ago

You are right. It would most likely deeply affect how FvwmCristal, NoCDE and other similar styling utilities work. I just mention it as an example of how what I propose could be convenient to deal with that kind of situations where you'll pass the exact command 5, 10 times just to do something as simple as force gnome apps to honor window decors.

I'm well aware that changing syntax out of the loop and in a radical manner would be problematic, both for developers and users. But as I said, the idea with this post/feature request is to get the conversation going. Get some input of what a new syntax should and should not be/have from both users and developers. Either way the decition of changing the syntax is any way is not mine at all.

What you propose seems like a viable solution, however is out of my league. Could you give us an example of how could it work?

Cheers :)

danespen commented 3 years ago

lgsobalvarro notifications@github.com writes:

You are right. It would most likely deeply affect how FvwmCristal, NoCDE and other similar styling utilities work. I just mention it as an example of how what I propose could be convenient to deal with that kind of situations where you'll pass the exact command 5, 10 times just to do something as simple as force gnome apps to honor window decors.

I'm well aware that changing syntax out of the loop and in a radical manner would be problematic, both for developers and users. But as I said, the idea with this post/feature request is to get the conversation going. Get some input of what a new syntax should and should not be/have from both users and developers. Either way the decition of changing the syntax is any way is not mine at all.

What you propose seems like a viable solution, however is out of my league. Could you give us an example of how could it work?

I'll use the ClickTime command as an example:

The syntax for that command is

ClickTime Delay

If the Delay is missing it uses DEFAULT_CLICKTIME. Otherwise if the Delay is less than zero, zero is used otherwise the value is converted from a string to an int. The value is stored in Scr.ClickTIme as an int. Then if Fvwm is in startup, the value is changed to a negative value.

So the table entry for parsing that command might be:

int min_params 0 int max_params 1 (For each parameter in this case there is only 1) enum stored_as INT void * stored_where Scr.ClickTIme int int_default_missing DEFAULT_CLICKTIME int min_value 0 int max_value MAX_INT

There would be a pointer in the functable (check functable.h, functable.c) that would point to the above structure.

To support other commands, more things would need to be added. (A lot more things.)

The last part, using a negative value during startup, could be handled with a table entry that says change to negative during startup or by pointing to a unique function to be called after the command is parsed.

The actual function CMD_ClickTime wouldn't be needed. The only thing that might require logic is the special logic for startup. That particular command doesn't do any checking so:

ClickTime Hello

isn't diagnosed. Using a table, I'd expect that it would be since "Hello" is not an int. Probably MAX_INT isn't a good max either. Easier to implement a reasonable max with a table.

-- Dan Espen

ThomasAdam commented 3 years ago

Hey @danespen,

Yeah, that's certainly one approach. To my mind, an enumeration table makes sense. We could expand this idea more generally though to consider that there are:

We could generalise the idea that for settings, they operate globally, or per screen or per desktop, or per page, and hence allow for a hierarchical inheritance of this. So for example, take DesktopSize, in FVWM3 this operates at a screen level (so you could say, has a screen context) hence a hypothetical command might be:

Commands which operate on a window could work similarly:

Move -t :0:XTerm 0 0

Would move the XTerm on the current screen on desktop 0, to position 0 0.

Move 0 0

Would work like Pick Move 0 0 does now, as no window was specified.

If each command was bounded by its understanding of how it is to operate, then we could easily insert defaults in to it upfront. So for example, with ClickTime, we could have an options table which looked like this:

struct options_entry {
    const char *name;
    const char *default_str;
    int default_num;
};

struct options_entry opt_table[] = {
    {
        .name = "ClickTime",
        .default_num = 250;
    }
};

Which means that if the user didn't specify a ClickTime, it defaults to 250. In terms of options, we could just walk the table (as you've said, @danespen), and allow for each option to define its own scope.

I'll get to command/window scoping later on, similar to fvwm's exec_context stuff later...

ThomasAdam commented 3 years ago

Leaving a few more thought here...

I was looking at decors earlier and realised that they're both very powerful and complex, yet utterly incomprehensible.

From the fvwm manpage:

                  AddToDecor FlatDecor
                  + ButtonStyle All Active (-- flat) Inactive (-- flat)
                  + TitleStyle  -- flat
                  + BorderStyle -- HiddenHandles NoInset

If you can tell me at a glance what the end result of that is, you should get commit-bit rights to fvwm3's repository. ;)

I think there's merit to trying to group things together -- I am loathe to go down the Yacc/Bison/Flex route of writing a grammar parser to describe the syntax of a config file. Perhaps using libconfig is easier?

danespen commented 3 years ago

Thomas Adam notifications@github.com writes:

Leaving a few more thought here...

I was looking at decors earlier and realised that they're both very powerful and complex, yet utterly incomprehensible.

From the fvwm manpage:

              AddToDecor FlatDecor + ButtonStyle All Active (--
              flat) Inactive (-- flat)

I think active/inactive are for buttons in use or grayed out due to application hints. The all means to apply it to all buttons.

              + TitleStyle -- flat

Not sure what a flat titlestyle is maybe no shadows.

              + BorderStyle -- HiddenHandles NoInset

The handles separate the border from the corners of the border. THere's an inset for the separation?

If you can tell me at a glance what the end result of that is, you should get commit-bit rights to fvwm3's repository. ;)

Well, that's my glance.

I think there's merit to trying to group things together -- I am loathe to go down the Yacc/Bison/Flex route of writing a grammar parser to describe the syntax of a config file. Perhaps using libconfig is easier?

Not sure what you're getting at.

All that decoration stuff was done before I got involved so I don't know any history on it.

-- Dan Espen

ThomasAdam commented 3 years ago

Hi @danespen

Yeah -- the decor syntax is really confusing. I've only ever had to use it to change a few things, as I stick to the MWM-style of window decorations anyway.

My point about Yacc/Bison was to highlight that we have a choice of either defining our own syntax for the proposal that @lgsobalvarro has suggested, or we look at existing configuration libraries, such as libconfig. Given the frustrations I've had with how fvwm does its syntax parsing, I'm keen to avoid too much rework of writing our own config file format using yacc. But I need to give it some more thought.

danespen commented 3 years ago

Thomas Adam notifications@github.com writes:

Hi @danespen

Yeah -- the decor syntax is really confusing. I've only ever had to use it to change a few things, as I stick to the MWM-style of window decorations anyway.

My point about Yacc/Bison was to highlight that we have a choice of either defining our own syntax for the proposal that @lgsobalvarro has suggested, or we look at existing configuration libraries, such as libconfig. Given the frustrations I've had with how fvwm does its syntax parsing, I'm keen to avoid too much rework of writing our own config file format using yacc. But I need to give it some more thought.

Ah, I see.

I've always done hand crafted parsers. More fun.

So far I haven't seen any syntax changes that appealed to me, but then I don't really want to make changes to my config anyway.

-- Dan Espen

lgsobalvarro commented 3 years ago

I joke about this on IRC yesterday night. Here is my "official" answer.

              + ButtonStyle All Active (-- flat) Inactive (-- flat)

Flattens button style when buttons are active and/or inactive.

              + TitleStyle  -- flat

Flattens Titlebar. No idea what exactly "flats" here.

              + BorderStyle -- HiddenHandles NoInset

Hides Handles?

I don't know. I have always used the MWM decor. So I have not really played with window decors. And without trying this out, I can't really tell what it does exactly.

ThomasAdam commented 3 years ago

I think what I'd like to see is a preprocessor approach to a new syntax, similar (but not in implementation) to what FvwmPerl does now.

For example, using lua, we could write a preprocessor for syntax which defined a config structure for:

style <...> {
    ...
}

function <...> {
    I {
        ....
    }
    C {
        ....
    }
}

etc...

... which then converts that to current fvwm syntax, sending it to fvwm dynamically (via FvwmMFL).

This would allow us to work on this independently of any deeper changes to Fvwm until we're happy with it.

codedmart commented 3 years ago

I don’t love lua (no strong opinion here yet), but I do like the approach you outlined @ThomasAdam.

ThomasAdam commented 3 years ago

It will be transparent to the user. It's not about forcing lua on people, this is the language choice for writing the preprocessor in for whatever DSL sort of thing we create. This has always been FVWM's approach; it's always had a DSL of sorts. Lua sounds reasonable as it has hooks in C, but also we could expand this for hook-support in the future.

lgsobalvarro commented 3 years ago

I think what I'd like to see is a preprocessor approach to a new syntax, similar (but not in implementation) to what FvwmPerl does now.

For example, using lua, we could write a preprocessor for syntax which defined a config structure for:

style <...> {
    ...
}

function <...> {
    I {
        ....
    }
    C {
        ....
    }
}

etc...

... which then converts that to current fvwm syntax, sending it to fvwm dynamically (via FvwmMFL).

This would allow us to work on this independently of any deeper changes to Fvwm until we're happy with it.

I think this is a really good idea. This approach would allow us to develop a new syntax in an "independent" way to any changes made to FVWM core, as you point out. Also it would allow to the new syntax evolve accordingly to feedback and user needs without the need of doing major changes to FVWM. Or so I gather from your comments.

danespen commented 3 years ago

lgsobalvarro notifications@github.com writes:

I think what I'd like to see is a preprocessor approach to a new syntax, similar (but not in implementation) to what FvwmPerl does now.

For example, using lua, we could write a preprocessor for syntax which defined a config structure for:

style <...> { ... }

function <...> { I { .... } C { .... } }

etc...

... which then converts that to current fvwm syntax, sending it to fvwm dynamically (via FvwmMFL).

This would allow us to work on this independently of any deeper changes to Fvwm until we're happy with it.

I think this is a really good idea. This approach would allow us to develop a new syntax in an "independent" way to any changes made to FVWM core, as you point out. Also it would allow to the new syntax evolve accordingly to feedback and user needs without the need of doing major changes to FVWM. Or so I gather from your comments.

I'm missing the value of it. How does it help users to give them 2 different syntaxes to deal with?

Is the new syntax easier to learn or understand, does it help to write configuration GUIs?

-- Dan Espen

lgsobalvarro commented 3 years ago

I'm missing the value of it. How does it help users to give them 2 different syntaxes to deal with? Is the new syntax easier to learn or understand, does it help to write configuration GUIs?

For what I gather, and @ThomasAdam can correct me if I'm wrong, it would be just one syntax the user would use. The other one (the current one) would be for FVWM "internal use".

I think what I proposed initially would be easier to learn and understand, if as a user you are at least a bit familiar with css or any similar markup that relays on blocks/brackets.

wyatt8740 commented 3 years ago

For what I gather, and @ThomasAdam can correct me if I'm wrong, it would be just one syntax the user would use. The other one (the current one) would be for FVWM "internal use".

This reminds me an awful lot of LISP S-Expressions and M-Expressions. That's not a terrible thing, but I just never see any dialects of LISP that really use M-Expressions these days (everyone basically settled on S-Expressions).

I think what I proposed initially would be easier to learn and understand, if as a user you are at least a bit familiar with css or any similar markup that relays on blocks/brackets.

My thoughts after reading through these comments:

I'd like to see it done as a translation into the internal syntax, if possible. For instance, if functions are declared like this

    # function definition
    do_a_thing() {
        command
    }

…That can be converted to the DestroyFunc do_a_thing/AddToFunc do_a_thing/+ syntax relatively easily by just listening for a closing curly brace with a stack. It also is reminiscent of shell scripts, which is an upshot in my opinion due to the high amount of use bourne shell scripts get in Unix environments. The () acts as a keyword to let the parser know that what follows will be a function definition.

If we want other kinds of "structured" data, either another kind of token could be used ([] for instance), or perhaps instead of using such tokens we could simply do something like what bash can optionally use for declaring functions:

function name [()] compound-command

…With function being the keyword for adding a function, and style being the keyword for adding styling information, for instance. A style keyword would then be parsed into a bunch of Style commands (or comma-separated values and a single Style command) until the closing } was reached.

This has the upshot of being more legible/less cryptic at a glance than, say, square brackets, at least to an average user of C-inspired languages or bash scripts (note that traditional bourne shell has no function builtin; this is a bash-ism. Bourne shell only specifies the func_name() {command} syntax).

For styles, therefore:

style xclock {
    Sticky
    !Title
    !Borders
    StaysOnTop
}
style * {
    Font "StringEncoding=UTF-8:xft:MS UI Gothic:size=9:bold:antialias=false,xft:Tahoma:size=8:bold:antialias=false"
}

…Would be equivalent to (and be decoded to) the traditional notation as either:

Style "xclock" Sticky
Style "xclock" !Title
Style "xclock" !Borders
Style "xclock" StaysOnTop

Style * Font "StringEncoding=UTF-8:xft:MS UI Gothic:size=9:bold:antialias=false,xft:Tahoma:size=8:bold:antialias=false"

Or, even better, simplified with commas to:

Style "xclock" Sticky, !Title, !Borders, StaysOnTop
Style * Font "StringEncoding=UTF-8:xft:MS UI Gothic:size=9:bold:antialias=false,xft:Tahoma:size=8:bold:antialias=false"

With the comma separation, we'd likely want to limit the length of individual lines to aid in debugging; for debugging purposes, the former translation might be better (and would certainly be easier to write). And with this notation, we could write functions as:

function FunctionName {
    +I Action
}

Translating to:

DestroyFunc FunctionName
AddToFunc FunctionName
+ I Action

…Perhaps this could be loaded from a separate "resource" file from the main FVWM config file by use of a new command (maybe called Parse, LoadResource, or ImportStructured? Just ideas; there's probably a better name out there, but this is a stream of consciousness post so I hope it makes sense/is coherent). The main upshot of the new format would be legibility at the cost of lower flexibility, which should rightly be a choice for the user to make. If desired, the user could simply have a one-line .fvwmrc/~/.fvwm/config file containing LoadResource $[FVWM_USERDIR]/structured-config. Of course, this way, using the traditional syntax it would still be possible to do additional AddToFunc's and similar as needed (or AddToFunc could be called from within the new format since it's translated when it's parsed). Alternatively a secondary keyword called append could be used which causes the parser to not generate the DestroyFunc line when followed with the function keyword.

I hope this was coherent and didn't ramble to much. I wrote it as I thought of things. Basically I think a shell-like syntax might be easier and also closer to the internal representation FVWM uses while retaining some benefits for end users in terms of the readability of things.

danespen commented 3 years ago

Wyatt Ward notifications@github.com writes:

For what I gather, and @ThomasAdam can correct me if I'm wrong, it would be just one syntax the user would use. The other one (the current one) would be for FVWM "internal use".

Yagg!

Think about it.

Are you going to duplicate all of Fvwm's internal error checking? If not, the user types one thing, Fvwm responds with an error message about some generated command nothing like what the user entered.

It's madness I tell you!

-- Dan Espen

wyatt8740 commented 3 years ago

Ah, that is fair. I didn't even think about how errors would be handled with this system. I'd probably have to have it be reversible, in that case… or have some automatically generated comments on the ends of lines that correlate them to the structured format line numbers.

Edit: Actually, upon reflection, didn't I basically just describe the kind of thing a C preprocessor would do? Maybe i should see how C compiler errors sourced from header file includes get traced back.

I suppose one could also add a way to tell fvwm to parse the config and print the result, including all parsed/sourced definitions, although that would still be like debugging at a different level than the source code (in an intermediary language).

Thanks for the input; this was basically an RFC (in the literal "request for comments" sense; just to get feedback) from me, anyway.

danespen commented 3 years ago

Wyatt Ward notifications@github.com writes:

Ah, that is fair. I didn't even think about how errors would be handled with this system. I'd probably have to have it be reversible, in that case… or have some automatically generated comments on the ends of lines that correlate them to the structured format line numbers.

Well, took me a while to connect the dots.

First time I saw someone implement something like that was some time in the 70s. Never let the back end do edits.

cpp barely pulls it off. Many the time I had to examine the cpp output to understand where compiler errors were coming from.

I think Fvwm is stuck with processing commands (as it should be).

-- Dan Espen

wyatt8740 commented 3 years ago

I think Fvwm is stuck with processing commands (as it should be). I'd be happy with leaving it alone and unchanged as well; I mainly wrote that up as a thought exercise. I have no (large) issues with how FVWM configuration currently works, since I am happy with how my desktop currently behaves.

I suppose that such a program would be easily implementable by the user, anyway, and might just be extra bloat and maintenance burden to include in the core functionality of FVWM.

Many the time I had to examine the cpp output to understand where compiler errors were coming from.

I edited my message and alluded to that as well… since I believe you're using email notifications, I'll quote it.

Edit: Actually, upon reflection, didn't I basically just describe the kind of thing a C preprocessor would do? Maybe i should see how C compiler errors sourced from header file includes get traced back. I suppose one could also add a way to tell fvwm to parse the config and print the result, including all parsed/sourced definitions, although that would still be like debugging at a different level than the "source code" (in effectively an intermediate language).

I 100% understand where you're coming from; I'm not convinced it's a good idea either, just potentially less disruptive than some of the other syntaxes people have mentioned in this issue since the actual FVWM command syntax would not need to change.

dwrl commented 3 years ago

On the danger of coming late to the party, and maybe missing something here:

I think there's merit to trying to group things together -- I am loathe to go down the Yacc/Bison/Flex route of writing a grammar parser to describe the syntax of a config file. Perhaps using libconfig is easier?

Maybe JSON would be an option? It is at least marginally human-readable, can hold nested data structures and can be used with or converted from a lot of formats and languages. (e.x. when I write python programs, I have all my settings in a dict, which I can simply push out into a JSON-file and be done with it, and read it in the same way, no grammar parsing required). Not knowing how FVWM handles it's config this might be an easy / accessible solution.

ThomasAdam commented 3 years ago

Maybe JSON would be an option?

No thanks. It's not very extensible, would become unweildy, and the JSON specification doesn't allow for comments.

s5k6 commented 3 years ago

Hm, sorry. I did not intend to write that much... But I'm really trying to get to a point in the end!

Aaaah, what syntax? A questinon that provides fertile grounds for disputes of religious dimensions. Good luck with that ;-)

No, honestly: I'm happy to see FVWM3 getting a DSL for configuration, and I sincerely believe this is a good idea. And a difficult task.

Let me elaborate:

All languages are crap, at some point at least. This can be easily verified by observing how many languages there are — if there was a really good one, then everybody would use it. But people are not happy with the languages they have, so they invent new ones. Even within a single use case (querying, programming), people cannot agree on a language.

And configuration? Well, none of the available languages seems to cut the mustard for FVWM3.

Don't use XML, nor JSON

Having worked on XML professionally (implemented Schema validation, built an XQuery compiler), I've got a pretty stong opinion about XML: It is pure shite. Overengineered and overly complex. Just try to read the entire standards body.

But there's one thing about XML that I want to point out, which distinguishes it from languages like JSON: XML allows to introduce new, named constructors. I'll explain what I mean by that.

At an abstract level, most languages (and all I'd consider relevant here) represent something with the structure of a rooted tree, or a directed acyclic graph.

Primitives are the nodes that have no outgoing edges, they are not constructed from other things. In C, an integer literal would be such a thing

42

or a variable

x

Then, each language provides means to construct new things from these primitives. In C, you can build an assignment

x = 42;

JSON also provides primitives (numbers, strings, …) and means to construct new things, like an object

{ "x": 42 }

But the things you can build in JSON are limited, it's lists and objects. Nothing more. Whether two objects are the same depends only on the members of the object. Contrast this with XML, where tags act as constructors:

<meter>42</meter>

<foot>42</foot>

This could represent two different objects, each constructed from (its own instance of) the same primitive 42, by using different constructors, meter and foot. And you can have any number of these constructors. In JSON? No can do. One might try

{ "unit": "meter", "value": 42 }

which is something different: It's just an object with two members. To be recognised as a length, a convention is required, like to look for a unit and a value member. When validated by a consumer, and transformed into something representing “42 meters”, then a reconstruction of a different datastructure has taken place. (And yes, not having comments in JSON is a major pain in the back.)

Being able to define and name constructors does not only allow to distinguish things which are identical on the inside. It also allows to create identical things in different ways, just think of Java classes having different constructors, like

Date(int year, int month, int date)
Date(long millisecondsSinceEpoch)
Date(String s)

which, as a side note, do have different names, because the signature is kinda part of the naming. Unfortunately.

I'd really hope that FVWM3's configuration would provide named constructors. I'll come back to that...

What I'd like to see in a config language

With the following list, one may wonder whether I'm not striving for a fully fledged programming language. And rightly so. It boils down to the question what we consider configuration. This is another issue of much debate.

An intermediate language

Having an “imtermediate” or “core” config language, as has been suggested, is certainly politically smart, because it does not slam the door in the face of anyone who does not like it (but in the end, most people will not like the config language anyways, see above).

It may not even be as insane as it looks at first: Many compilers use an intermediate representation to compile the input language to.

Of course, each compiler frontend does all the checking it can do, trying hard not to produce a flawed intermediate representation (which produces awkward error messages when it happens). But this is likely the only way to do it, some errors may not even be practial to find in some representations.

But the reason to use intermediate representations is rather technical, than political: When different language features may be expressed by the same means of a core language, then “desugaring” makes writing a compiler easier. E.g., in C we can replace

a[i]   ↦   *(a + i)

or in Haskell, where the entire construct of list comprehension can be removed from the syntax

  [ x + 1 | x <- [1..100], even x ]
↦
  concatMap (\x -> if (even x) then [x + 1] else []) [1..100]

Also, intermediate languages should be crafted in a way to make desirable operations easier, or to expose properties not easily accessible in the surface language.

What does this mean here?

An intermediate configuration language should not be crafted to be very easy to read, but rather to be minimal in the number of means it provides, and close to the structures iniside FVWM3 that need to be controlled. This could be extremely simple plain text reverse polish notation (RPN) which lends itself nicely to build trees and DAGs. Or less simple RPN, as in PostScript. But it could also be something more standardised, like ASN.1 or Protocol Buffers (both of which I've never used).

But in any case, this intermediate language should be really well designed, i.e., it must be able to express all configuration needs that FVWM3 has, be it commands, or description of styles or menus, and what not.

Which brings me to the final point:

First, define an API

I think it would be helpful to have a very clear picture of what the config language should be able to express. Formal, detailed and precise. So much so, that it is basically an API.

This API must be the only interface to configure FVWM3.

Thus, FVWM3 being written in C, C would also be FVWM3's first prototypical configuration language.

The functions, values and types provided by this API represent the constructors and primitives mentioned above. C adds its means of abstration and combination, and a simple type system.

If time proves the API stable, and simple, and elegant enough, it would become clear what features a config language should provide.

If the API is clean and complete, in the sense that everything that FVWM3 must be able to digest can be fed in through that API conveniently, then designing a suitable surface language should follow naturally, I hope.

Any disputes about syntax should be postponed until then.

The plan would be to consider the API the intermediate language, but instead of compiling a configuration DSL into C code, the DSL would be interpreted into API calls.

I cannot tell what the result will be. Maybe it turns out to be Lua (God help us), or a Python library that feeds ASN.1 into FVWM3 via a socket, or a nice DSL like the one for FVWM2, which has proven very usable for me.

wyatt8740 commented 3 years ago

Seems to me you're basically suggesting that we do FVWM configuration in Haskell or C++, since you mention constructors and abstraction (i.e., implied concept of objects).

If we really do want to make things annoying and obtuse for early adopters that don't know a ton about programming already, and also wish to totally break compatibility with configurations that we already have, then I have my own suggestions.

I know people who use and know Haskell certainly exist, but if we're talking about these sorts of languages, and since you mentioned favoring the removal of "List comprehension" as a concept, I'd suggest the opposite. Make everything a list. I think you'll find there are far more people who have had at least a basic exposure to Lisp (mainly due to the influence of emacs), so I think some variant thereof might be preferable.

The basic concept of configuring a complex and flexible program via Lisp S-expressions has already been shown (again, emacs).

Lisp in particular strikes me as a decent choice because it is capable of being prototypical, declarative, imperative, and anything else anyone wants, while also being highly simple in its basic premise.

Also, assuming there's a 1:1 mapping between the existing commands and Lisp symbol names, this would make translating existing configurations relatively easy while also allowing for far greater flexibility without necessarily depending on calling external scripts. And if the Lisp side of things was given the ability to interface directly with X (via Xlib or XCB, or via something like clx for Common Lisp), then that's a foothold to allow adding new features/functionality via the Lisp side of things.

Of course, to accomplish this, a truly massive amount of work would have to be done to either bind the C code to Lisp or to rewrite most of the program in a Lisp dialect. It also requires either implementing or integrating an existing Lisp. So this is all at least as unlikely in my mind as the suggestion above.

again, I honestly don't think this will or even necessarily should happen, and the status quo still seems at the very least acceptable to me.

The idea of translating to an intermediary language seems a little scary to me, and this is as someone who has and loves HP RPL calculators. They're great for what they can do, but I would never want to debug something as massive as my FVWM configuration in something like RPL or Forth, which would make debugging a nightmare.

lgsobalvarro commented 3 years ago

Ok, I think we are going a bit off track here. So lets go back a bit and remember what the goals are taking into account some really important comments that have been made here.

  1. Streamline the current config syntax (get rid of duplicated commands, deprecated ones and so on).
  2. Maintain compatibility with the current syntax. As @danespen points out FVWM is command driven and themes and such relays on this. (aka: If I prefer to have a command driven config, as it works right now, I can).
  3. Using the current syntax as a base come up with an alternative way to state your config via blocks. Hence my CSS/SASS analogy. This with the intention of begin able to have shorter config files (due nesting and such concepts, I could "group" several styles for different apps that share common properties and avoid repetition, for instance. Eg: Style Foo !Title, Style Bar !Title ), and more importantly make it more friendly for newcomers. Also try to avoid situations like this example @ThomasAdam give us (I'm sure we all have a good idea what that does, but not exactly what, just by looking at it.)
ThomasAdam commented 3 years ago

Hi everyone,

Thanks for the comments thus far. I don't want to see a DSL implemented. The barrier to entry for users is already far too high, and enforcing what amounts to a programming language would push too many people away -- so we will try and minimise that as much as possible.

I'm interested in only the following:

I see that more around command-line style syntax, as in:

move -s :2 -w res:/XTerm/ -t MONITOR2:4

Whereby "-s" is a source desktop (2 in this case, on the current monitor), moving all windows which have a resource of XTerm somewhere in the string, to desktop 4 on MONITOR2.

Once you've worked out how the source/target parameters can be consistently applied, then you have the automatic notion of context -- for things like windows, desktops, mouse, etc.

Functions remain definable in their own right -- perhaps the syntax changes to a block-scope style, I don't know.

But none of this is fundamentally moving the user toward a DSL. It's enforcing consistency with functionality.

s5k6 commented 3 years ago

No, I'm not suggesting to “do FVWM configuration in Haskell or C++”, to the contrary, I'm advising against it. My entire point is, that the first goal should be to build an API in C that allows convenient configuration in the sense that it is a simple API with only a few, simple concepts that combine nicely. Not to make the user write C for configuration, but to guide the implementation of the configuration system.

FVWM's config is already a DSL, it's just not turing complete. And I would be wary to suggest a turing complete language. If you'd want to go there, build an interface to Python or Lua. This should even be possible against said API…

ThomasAdam commented 3 years ago

Hi @s5k6

I do understand your PoV, I just don't want to turn FVWM into a DSL. I know you can argue that any application has elements of DSL about them, and FVWM certainly does. But I am more interested right now in making the command subset FVWM has to be more akin to something like tmux.

After the fact, if language bindings become popular, that's a separate issue.

At this point, I think we've all chatted enough about this. Is there anyone who's going to help me code this, or is it all rhetoric? That'll be where the real value comes from -- the actions to go with the words mentioned here. :) If there's any takers, do please let me know!

s5k6 commented 3 years ago

Sorry, another week has passed, and I did not find enough time.

I was looking into the code (briefly!), but I have no clear idea of the concepts that are implemented. What I'm getting (maybe incorrectly) is, that reading a file and executing the commands therein happens around the function run_command_stream in read.c, right?

I got a bit lost in __execute_function in functions.c, which seems to be still occupied with some kind of parsing, using functions named PeekToken. What is the idea behind this?

ThomasAdam commented 3 years ago

Hi @s5k6

Sure -- I know most of us are busy! Have a look here for some notes Dominik and I put together a while ago: https://github.com/fvwmorg/fvwm3/blob/master/dev-docs/PARSING.md