Zettlr / Zettlr

Your One-Stop Publication Workbench
https://www.zettlr.com
GNU General Public License v3.0
10.35k stars 635 forks source link

Feature Request: command line parameter for opening a new file. #297

Closed yennor closed 4 years ago

yennor commented 5 years ago

If you want to use Zettlr as note taking application it would be great if it would support a global shortcut-key to open a new note. But probably it is a lot easier to just support a command line parameter for opening a new note. Then the user can create a global shortcut using his desktop gui which will call zettlr with the according parameter. Propably it should also be possible to choose, in which directory be default the new file should be created.

Right now it would be possible to do that, using two shortcuts: e.g. ctrl-alt-z to open zettlr, and then pressing again ctrl-n to open the new file. which is a bit annoying.

nathanlesage commented 5 years ago

Huh, I seem to have overlooked this issue. Nevertheless, calling zettlr path/to/file.md should work since the first version! Even with multiple files

willuhn commented 4 years ago

It doesn't work for me either. Tested with 1.6 and 1.7 beta5 in Linux. Do i need any special configuration settings for this?

willuhn commented 4 years ago

It works, if i open a second instance of the program. According to source/main.js, there are 2 locations, where "global.filesToOpen" is filled: line 116 - when starting a second instance and line 129, when the "open-file" is fired. This event probably doesn't work. After some googling for "app.on('open-file'" I found this issue: https://github.com/electron/electron/issues/4403 (seems to be mac specific. Maybe it also happens to linux. Wrapping the call into 'will-finish-launching' could probalbly help:

app.on('will-finish-launching', function() {
    app.on('open-file', function(ev, path) {
        //...
    });
});
nathanlesage commented 4 years ago

Yes, the open-file-event is macOS-specific. Nevertheless, the secondInstance-code solves this problem for all other platforms as well, as basically the first running instance requests a lock, after which every subsequent application will silently quit, whereas the argv of it are propagated to the first running instance, thereby calling the code in the on-second-instance-event handler

willuhn commented 4 years ago

Maybe I don't understand the code. In https://github.com/Zettlr/Zettlr/blob/develop/source/main/zettlr.js#L177 the given files are being opened. But I can't find the location, where "global.filesToOpen" is filled for the first instance. Does this really work for you?

In my opinion, "global.filesToOpen" is only filled when launching a second instance because it's inside of "app.on('second-instance')":

app.on('second-instance', (event, argv, cwd) => {
  // Retrieve all potential files from the list of arguments. Thanks to
  // Abricotine for this logic!
  // Taken from: https://github.com/brrd/Abricotine/blob/develop/app/abr-application.js
  argv = argv && argv.length > 0 ? argv : process.argv
  let files = argv.filter(function (element) {
    return element.substring(0, 2) !== '--' && isFile(element) && !ignoreFile(element)
  })
  [...]
  if (zettlr) {
    [...]
    // handover files to first instance
    zettlr.handleAddRoots(files)
  } else {
    // IMHO unreachable code since it's called on "second-instance"
    // But in this case, "zettlr" cannot be null
    global.filesToOpen = global.filesToOpen.concat(files)
  }
nathanlesage commented 4 years ago

Have a look here: https://github.com/Zettlr/Zettlr/blob/develop/source/main.js#L112

In my opinion, "global.filesToOpen" is only filled when launching a second instance because it's inside of "app.on('second-instance')"

Exactly, but remember that the second-instance-event is fired on the first instance. This is basically just security to prevent race conditions where the app is launched twice immediately after each other, because then the first instance will still be booting (the app-ready event has not yet fired) when the second will be shutting down. In this case we need this array. There is, put short, a short timespan where the application is not yet loaded (e.g. handleAddRoots cannot yet be called) whereas the lock has already been established.

willuhn commented 4 years ago

I've seen this. But is it really executed on the first instance also? Since int's inside the handling of "app.on('second-instance')"

nathanlesage commented 4 years ago

Yes, see my edit I just published

nathanlesage commented 4 years ago

Maybe this badly written schematic will help understand this:

First app boots
  |
  |           Second app boots
firstInstanceLock called
  |                   |
  |               firstInstanceLock not established -> exit()
  |
onSecondInstance called
  |
  |
  app ready-event fires
willuhn commented 4 years ago

Thanks for your explanation. I'm trying to further investigate, why it does not work on my system.

spacekaila commented 4 years ago

I just tried this and when I run zettlr in the command line (with or without files), I get bash: zettlr: command not found.

MacOS 10.15.5, Zettlr 1.7.0-beta.8

nathanlesage commented 4 years ago

@spacekaila That won't work because you'd need to run /Applications/Zettlr.app/Contents/MacOS/Zettlr, or put a symlink into your bin-directory to put the executable into your PATH.

@willuhn I just fiddled a little bit with the code of running Zettlr, and now it should work; at least I tested it and it worked out!

spacekaila commented 4 years ago

@nathanlesage I added a symlink, but now if I run zettlr (with or without adding a filename after) I get the following error

(base) mycomputer:~ spacekaila$ zettlr
FSAL state changed: filetree
FSAL state changed: openFiles
FSAL state changed: openDirectory
FSAL state changed: activeFile
627 ms: Loaded directory /my/open/directory
Starting chokidar ...
FSAL state changed: filetree
FSAL state changed: openDirectory
FSAL state changed: openFiles
+++++ SYNCING OPEN FILES WITH RENDERER +++++
[29126:0601/122653.789655:FATAL:electron_main_delegate_mac.mm(71)] Unable to find helper app
[29128:0601/122653.878201:FATAL:electron_main_delegate_mac.mm(71)] Unable to find helper app
[29129:0601/122653.968676:FATAL:electron_main_delegate_mac.mm(71)] Unable to find helper app

the FATAL error just keeps printing until I end the process. Zettlr opens files, but I can't edit them (typing just doesn't do anything). I can still open and use zettlr normally by clicking on the icon.

nathanlesage commented 4 years ago

Ah interesting. This seems to indicate that Electron uses the cwd path variable to determine the location of the helper app, and if you call it from somewhere else on your file system, the cwd obviously is something different. Interesting, I've never started the built app from the command line, so I don't know how Electron behaves in that case!

spacekaila commented 4 years ago

Ah, that's interesting.

If anyone else comes across this and wants to be able to use Zettlr in the command line, my solution was to just add an alias in my .bash_profile as such

alias zettlr="/Applications/Zettlr.app/Contents/MacOS/Zettlr"

not the most elegant solution, but it works.

willuhn commented 4 years ago

@willuhn I just fiddled a little bit with the code of running Zettlr, and now it should work; at least I tested it and it worked out!

I did a fresh checkout from github, built it with "yarn build:quick" and started "release/linux-unpacked/zettlr". Unfortunately unchanged behaviour. Opening a file via command-line only works for the second instance. "~/.config/zettlr/logs/.log" doesn't show any errors. Can I increase the loglevel somehow?

nathanlesage commented 4 years ago

~/.config/zettlr/logs/.log

That's not where the application logs are stored, these are under /Users/<name>/Application Support/Zettlr/logs, but I doubt that you will find anything. The missing piece of information was actually "Opening a file via command-line only works for the second instance."

Because this now solves the riddle: As far as I know I never implemented behaviour to filter out the command line arguments from the first instance to begin with. Should be fairly simple to port.

willuhn commented 4 years ago

I thought, ~/.config/zettlr/logs/.log is the linux counterpart of MacOS' /Users/<name>/Application Support/Zettlr/logs. At least for me, Zettlr stores all data in ~/.config/zettlr

Because this now solves the riddle: As far as I know I never implemented behaviour to filter out the command line arguments from the first instance to begin with. Should be fairly simple to port.

Ah ok. That explains the thing.

nathanlesage commented 4 years ago

At least for me, Zettlr stores all data in ~/.config/zettlr

Ah, wait, so you're using Linux? Then disregard what I said!