CDSoft / pp

PP - Generic preprocessor (with pandoc in mind) - macros, literate programming, diagrams, scripts...
http://cdelord.fr/pp
GNU General Public License v3.0
252 stars 21 forks source link

relative paths of includes markdown files should be rewritten #60

Closed ds82 closed 5 years ago

ds82 commented 6 years ago

I have the following project structure:

/doc/main.md
/subproject-1/doc/sub-1.md
/subproject-1/doc/image.png
/subproject-2/doc/sub-2.md
/subproject-2/doc/image.png

In main.md I include the sub-*.md files

!include(../subproject-1/doc/sub-1.md)
!include(../subproject-2/doc/sub-2.md)

and sub-*.md has an image with a relative path:

![](./image.png)

The preprocessed markdown file in /doc/ still uses the relative image path ./image.png, but this path does not exist in /doc/ ..

Any suggestions how to handle this?

tajmone commented 6 years ago

EDITED (fixed error in example: !def(path2root)(../..))


This is the expected behavior (ie, that the working path is the one of the invoking script).

If you're reusing (including) subfoldered documents inside different main documents (stored at different levels of the folder tree) this might get messy.

Probably you should implement a custom macro always pointing to the project's root (via one or more ../, or ./), which you can then add to the images' path. Example, for a doc built From main/ folder:

!def(path2root)(..)

![](!path2root/subproject/doc/image.png)

NOTE: no slash in !def(path2root)(..) after .. because there is already a slash in !path2root/doc (although in most OSs an extra slash shouldn't cause any problems).

and when building a doc from /subproject/doc/:

!def(path2root)(../..)

![](!path2root/subproject/doc/image.png)

... and so on.

!path2root will be redefined in various places of the documents toolchain, depending on the current working path, so that it will always provide a relative path back to the root (eg: "./, "../", etc.). This would allow you to point to any assets relatively from the project root, and since you can change !path2root at will (either from the docs or the CLI options), you'll be able to gain control on how each document can refer to assets relatively to the root folder.

Maybe this could be a viable solution. I've used it in various project (either using PP macros directly, or using env vars injection via PP).

ds82 commented 6 years ago

Thanks for your detailed answer .. I'll see if I can use macros to work around this.

Still, I don't understand why exactly it would be wrong to rewrite all relative paths inside included files(?). This way it would always work, even if I include the sub-files in different main-files located in different dirs ... or am I missing something?

The behaviour now does only work correctly (for realtive image paths) if the main and the sub file are in the same folder ..

tajmone commented 6 years ago

(I've realized the example had an error and I fixed, I must have hit CtrlZ by mistake while editing!)

why exactly it would be wrong to rewrite all relative paths inside included files(?).

I don't think that there would be anything wrong with that, but definitely it would be a major change breaking backward compatibility and existing projects would have to be fixed accordingly; therefore I preferr to leave any considerations on that to @CDSoft (I'm only a user of PP, not a maintainer).

Personally, I think that instead of changing the current behavior of !include it might be better to add an alternative built-in macro (eg, !xinclude) to implement what you propose — ie, that for the included doc its working directory would be the path where it resides, instead of the path of the invoking script. This way we wouldn't break backward compatibility. Probably there are many considerations to be kept into account for this (eg, after x-inclusion is over, should the working path be restored to the value it had before? are there pitfalls with nested inclusions? could this affect the behavior in the rest of the invoking script? etc.)

I think that this is a common problem in big projects. I've come across it many times, and usually the way I went about it was to have an initialization script which would set and environment variable to the absolute path of the project's root, and then inject that env var via PP's !env before any assets path.

For example, in batch scritps:

SET "PP_MACROS_PATH=%~dp0"

and in Bash:

export PP_MACROS_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/"

and then in the actual PP macros:

!define(GFMAlertsInlineCSS)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<style type="text/css">
!rawinc(!env(PP_MACROS_PATH)GFM-Alerts.css)
</style>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Alternatively, I used the approach mentioned above.

The fact is that different users and project will probably require different approaches to such problems. Overall, I think that the current behavior reflects the common expectations, but definitely what you're proposing would be very useful in many situations.

PP is flexible enough to allow different solutions for this problem, and also keep in mind that you can always change env vars or use a shell/CMD command to change the current working directory, so you could define a custom macro variant of !include (eg, !my_inc) that changes the current working directory before including the doc; you could then use !my_inc to include docs instead of PP's native macro.

Having said that, I'm with you for the proposed change (as a new alternative macro though, not changing the default behavior); so let's see what @CDSoft replies.

bpj commented 6 years ago

The most useful thing might be a builtin macro which returns the path to the directory containing the current file.

tajmone commented 6 years ago

The most useful thing might be a builtin macro which returns the path to the directory containing the current file.

Excellent idea!

CDSoft commented 5 years ago

Hello,

First of all you must be aware that relative image paths are relative to:

So modifying paths at preprocessing time may not be a good idea.

A macro returning the path of the current file seems to be a good compromise as the user can control everything, whatever the desired output format.

There are already two macros (!main and !file) that gives the absolute or relative (according to the kind of path given on the command line) of the main file (the one given on the command line) and the current file (the ones that were, directly or not, included from the main file).

A quick workaround is !file/.. to get the directory containing !file or !file/../.. to get the parent directory. A macro returning !file/.. would certainly be more readable. !main/.. could be useful as well.

CDSoft commented 5 years ago

What about !cwd (for current working directory) and !root for the directory of the main input file. They could be defined as !def(cwd)(!file/..) and !def(!root)(!main/..) but as built-in macros in a more portable way.

CDSoft commented 5 years ago

What about !cwd (for current working directory) and !root for the directory of the main input file. They could be defined as !def(cwd)(!file/..) and !def(!root)(!main/..) but as built-in macros in a more portable way.

CDSoft commented 5 years ago

done in pp 2.5