Skip to content
This repository has been archived by the owner on Apr 1, 2020. It is now read-only.

Sort out CLI for Oni #2372

Merged
merged 12 commits into from
Jul 1, 2018
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions browser/src/Platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,18 @@ export const isLinux = () => os.platform() === "linux"

export const getUserHome = () => (isWindows() ? process.env["APPDATA"] : process.env["HOME"]) // tslint:disable-line no-string-literal

export const getLinkPath = () => (isMac() ? "/usr/local/bin/oni" : "") // TODO: windows + linux
export const getLinkPath = () => (isMac() ? "/usr/local/bin/oni" : "") // TODO: Linux
export const isAddedToPath = () => {
if (isMac()) {
try {
fs.lstatSync(getLinkPath())

const currentLinkPath = fs.readlinkSync(getLinkPath())

// Temporary guard to check if the old script has been linked to.
if (currentLinkPath.indexOf("cli/mac/oni.sh") === 0) {
return false
}
} catch (_) {
return false
}
Expand All @@ -21,14 +28,14 @@ export const isAddedToPath = () => {

return false
}
export const removeFromPath = () => (isMac() ? fs.unlinkSync(getLinkPath()) : false) // TODO: windows + other
export const removeFromPath = () => (isMac() ? fs.unlinkSync(getLinkPath()) : false) // TODO: Linux

export const addToPath = async () => {
if (isMac()) {
const appDirectory = path.join(path.dirname(process.mainModule.filename), "..", "..")
const options = { name: "Oni", icns: path.join(appDirectory, "Resources", "Oni.icns") }
const linkPath = path.join(appDirectory, "Resources", "app", "oni.sh")
await _runSudoCommand(`ln -s ${linkPath} ${getLinkPath()}`, options)
const linkPath = path.join(appDirectory, "Resources", "app", "cli", "mac", "oni.sh")
await _runSudoCommand(`ln -fs ${linkPath} ${getLinkPath()}`, options)
}
}

Expand Down
4 changes: 3 additions & 1 deletion build/BuildSetupTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const packageJsonContents = fs.readFileSync(path.join(__dirname, "..", "package.
const packageMeta = JSON.parse(packageJsonContents)
const { version, name } = packageMeta
const prodName = packageMeta.build.productName
const pathVariable = "{app}\\resources\\app\\cli\\windows\\"

let buildFolderPrefix = os.arch() === "x32" ? "ia32-" : ""

Expand All @@ -37,10 +38,11 @@ const valuesToReplace = {
SourcePath: path.join(__dirname, "..", "dist", `win-${buildFolderPrefix}unpacked`, "*"),
WizardImageFilePath: path.join(__dirname, "setup", "Oni_128.bmp"),
WizardSmallImageFilePath: path.join(__dirname, "setup", "Oni_54.bmp"),
cliPath: pathVariable,
}

const addToEnv = `
Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}"; Tasks: addtopath; Check: NeedsAddPath(ExpandConstant('{app}'))
Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};${pathVariable}"; Tasks: addtopath; Check: NeedsAddPath(ExpandConstant('${pathVariable}'))
Root: HKCU; Subkey: "SOFTWARE\\Classes\\*\\shell\\${prodName}"; ValueType: expandsz; ValueName: ""; ValueData: "Open with ${prodName}"; Tasks: addToRightClickMenu; Flags: uninsdeletekey
Root: HKCU; Subkey: "SOFTWARE\\Classes\\*\\shell\\${prodName}"; ValueType: expandsz; ValueName: "Icon"; ValueData: "{app}\\resources\\app\\images\\oni.ico"; Tasks: addToRightClickMenu
Expand Down
83 changes: 83 additions & 0 deletions build/setup.template.iss
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,88 @@ begin
Result := Pos(';' + Param + ';', ';' + OrigPath + ';') = 0;
end;
// http://stackoverflow.com/a/23838239/261019
procedure Explode(var Dest: TArrayOfString; Text: String; Separator: String);
var
i, p: Integer;
begin
i := 0;
repeat
SetArrayLength(Dest, i+1);
p := Pos(Separator,Text);
if p > 0 then begin
Dest[i] := Copy(Text, 1, p-1);
Text := Copy(Text, p + Length(Separator), Length(Text));
i := i + 1;
end else begin
Dest[i] := Text;
Text := '';
end;
until Length(Text)=0;
end;
// This update step checks for the old path variable ({app}) and removes it when installing if needed.
// This is a modified version of the UninstallStepChanged, taken from VSCode.
procedure CurStepChanged(CurUninstallStep: TSetupStep);
var
Path: string;
OldOniPath: string;
Parts: TArrayOfString;
NewPath: string;
i: Integer;
begin
if not CurUninstallStep = ssInstall then begin
exit;
end;
if not RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Path)
then begin
exit;
end;
NewPath := '';
OldOniPath := ExpandConstant('{app}')
Explode(Parts, Path, ';');
for i:=0 to GetArrayLength(Parts)-1 do begin
if CompareText(Parts[i], OldOniPath) <> 0 then begin
NewPath := NewPath + Parts[i];
if i < GetArrayLength(Parts) - 1 then begin
NewPath := NewPath + ';';
end;
end;
end;
RegWriteExpandStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', NewPath);
end;
// This is taken from the VSCode installer file.
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
Path: string;
OniPath: string;
Parts: TArrayOfString;
NewPath: string;
i: Integer;
begin
if not CurUninstallStep = usUninstall then begin
exit;
end;
if not RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Path)
then begin
exit;
end;
NewPath := '';
OniPath := ExpandConstant('{{cliPath}}')
Explode(Parts, Path, ';');
for i:=0 to GetArrayLength(Parts)-1 do begin
if CompareText(Parts[i], OniPath) <> 0 then begin
NewPath := NewPath + Parts[i];
if i < GetArrayLength(Parts) - 1 then begin
NewPath := NewPath + ';';
end;
end;
end;
RegWriteExpandStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', NewPath);
end;
[Registry]
{{RegistryKey}}
19 changes: 19 additions & 0 deletions cli/linux/oni.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash

# Get the path to the currently running script:
self=$0

# Test if $self is a symlink
if [ -L "$self" ] ; then
# readlink returns the path to the file the link points to:
target=$(readlink "$self")
else
target=$self
fi

ONI_PATH=$(dirname "$target")
ONI_EXECUTABLE="${ONI_PATH}/../../../../oni"
CLI_SCRIPT="${ONI_PATH}/../../lib/cli/src/cli.js"

ELECTRON_RUN_AS_NODE=1 "${ONI_EXECUTABLE}" "$CLI_SCRIPT" "$*"
exit $?
19 changes: 19 additions & 0 deletions cli/mac/oni.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash

# Get the path to the currently running script:
self=$0

# Test if $self is a symlink
if [ -L "$self" ] ; then
# readlink returns the path to the file the link points to:
target=$(readlink "$self")
else
target=$self
fi

ONI_PATH=$(dirname "$target")
ONI_EXECUTABLE="${ONI_PATH}/../../../../MacOS/Oni"
CLI_SCRIPT="${ONI_PATH}/../../lib/cli/src/cli.js"

ELECTRON_RUN_AS_NODE=1 "${ONI_EXECUTABLE}" "$CLI_SCRIPT" "$*"
exit $?
29 changes: 0 additions & 29 deletions cli/oni

This file was deleted.

82 changes: 82 additions & 0 deletions cli/src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as path from "path"

import { CLIArgs, parseCLIProcessArgv } from "./cli_args"

import { spawn } from "child_process"

export async function main(cli_arguments: string[]): Promise<any> {
// First, try to parse the CLI args and deal with them.
let args: CLIArgs

try {
args = parseCLIProcessArgv(cli_arguments)
} catch (err) {
process.stdout.write(err.message)
throw err
}

if (args.help || args.version) {
const version = require(path.join(__dirname, "..", "..", "..", "package.json")).version // tslint:disable-line no-var-requires
process.stdout.write("Oni: Modern Modal Editing - powered by Neovim\n")
process.stdout.write(` version: ${version}\n`)
process.stdout.write("\nUsage:\n oni [FILE]\t\tEdit file\n")
process.stdout.write("\nhttps://github.com/onivim/oni\n")

return Promise.resolve(true)
}

// Otherwise, was loaded normally, so launch Oni.

const env = assign({}, process.env, {
ONI_CLI: "1", // Let Oni know it was started from the CLI
ELECTRON_NO_ATTACH_CONSOLE: "1",
})

delete env["ELECTRON_RUN_AS_NODE"]

const options = {
detached: true,
env,
}

const child = await spawn(process.execPath, cli_arguments.slice(2), options)
child.unref()

child.on("close", code => {
if (code !== 0) {
throw Error(`Exit code was ${code}, not 0.`)
} else {
return Promise.resolve(true)
}
})

child.on("error", err => {
throw err
})

child.on("exit", code => {
if (code && code !== 0) {
throw Error(`Exit code was ${code}, not 0.`)
} else {
return Promise.resolve(true)
}
})
}

function assign(destination: any, ...sources: any[]): any {
sources.forEach(source => Object.keys(source).forEach(key => (destination[key] = source[key])))
return destination
}

function eventuallyExit(code: number): void {
setTimeout(() => process.exit(code), 0)
}

main(process.argv)
.then(() => {
eventuallyExit(0)
})
.then(null, err => {
console.error(err.message || err.stack || err)
eventuallyExit(1)
})
27 changes: 27 additions & 0 deletions cli/src/cli_args.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as minimist from "minimist"

export interface CLIArgs {
help?: boolean
version?: boolean
[arg: string]: any
}

const options: minimist.Opts = {
boolean: ["help", "version"],
alias: {
help: "h",
version: "v",
},
}

export function parseArgs(args: string[]): CLIArgs {
return minimist(args, options) as CLIArgs
}

// Skip the first 2 arguments as that is the location of the Electron binary,
// and the location of the cli.ts script.
export function parseCLIProcessArgv(processArgv: string[]): CLIArgs {
let [, , ...args] = processArgv

return parseArgs(args)
}
29 changes: 29 additions & 0 deletions cli/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"compilerOptions": {
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"jsx": "react",
"lib": ["dom", "es2017"],
"module": "commonjs",
"moduleResolution": "node",
"newLine": "LF",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"outDir": "../lib/cli",
"pretty": true,
"removeComments": true,
"rootDir": ".",
"skipLibCheck": true,
"strictNullChecks": false,
"suppressImplicitAnyIndexErrors": true,
"target": "es2015",
"sourceMap": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}
5 changes: 5 additions & 0 deletions cli/windows/oni.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@echo off
setlocal
set ELECTRON_RUN_AS_NODE=1
call "%~dp0..\..\..\..\Oni.exe" "%~dp0..\..\lib\cli\src\cli.js" %*
endlocal
12 changes: 1 addition & 11 deletions main/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as minimist from "minimist"
import * as path from "path"

import { app, BrowserWindow, ipcMain, Menu } from "electron"
Expand All @@ -23,16 +22,7 @@ if (isAutomation) {
Log.info("Verbose flag set since running automation")
}

// We want to check for the 'help' flag before initializing electron
const argv = minimist(process.argv.slice(1))
const version = require(path.join(__dirname, "..", "..", "..", "package.json")).version // tslint:disable-line no-var-requires
if (argv.help || argv.h) {
process.stdout.write("ONI: Modern Modal Editing - powered by Neovim\n")
process.stdout.write(` version: ${version}\n`)
process.stdout.write("\nUsage:\n oni [FILE]\t\tEdit file\n")
process.stdout.write("\nhttps://github.com/onivim/oni\n")
process.exit(0)
}
// const argv = minimist(process.argv.slice(1))

interface IWindowState {
bounds?: {
Expand Down
Loading