Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
38 changes: 35 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ The result is shown only after the execution is finished. It is not possible to

<hr></div>

The following [languages are supported](#supported-programming-languages-): C, C++, CSharp, Dart, F#, Golang, Groovy, Haskell, Java, JavaScript, Kotlin, Lean, Lua, Maxima, OCaml, Octave, Prolog, Python, R, Racket, Ruby, Rust, Scala, Shell (including Batch & Powershell), SQL, TypeScript, Wolfram Mathematica, Zig.
The following [languages are supported](#supported-programming-languages-): C, C++, CSharp, Dart, F#, Golang, Groovy, Haskell, Java, JavaScript, Julia, Kotlin, Lean, Lua, Maxima, OCaml, Octave, Prolog, Python, R, Racket, Ruby, Rust, Scala, Shell (including Batch & Powershell), SQL, TypeScript, Wolfram Mathematica, Zig.

If you are new to MarkDown or Obsidian.md, you can go to the [Quickstart Guide](#quickstart-guide-) or take a look in to [some blogs and videos that feature this plugin](#featured-in)

Python, R, and Octave support embedded plots. All languages support ["magic" commands](#magic-commands-) that help you to access paths in obsidian or show images in your notes.
Python, R, Julia, and Octave support embedded plots. All languages support ["magic" commands](#magic-commands-) that help you to access paths in obsidian or show images in your notes.

You can create code blocks that are executed before or after each code block of the same language and define [global code injections](#global-code-injection-and-reusing-code-blocks-).

Expand Down Expand Up @@ -137,6 +137,38 @@ plot(x, y, type="l")
```
</details>

<details>
<summary>Julia</summary>

- Requirements: Julia is installed and the correct path is set in the settings. Recommend using [juliaup](https://julialang.org/install/).
```julia
println("Hello, world!")
```
- By default, Julia runs in Notebook Mode. You can turn this off in the settings.
- Plots with Plots.jl are embedded in the note by default. You can turn this off in the settings. Note that plots are only embedded when `display(current())` is called.
```julia
using Plots
x = range(0, 10, length=100); y = sin.(x); plot(x, y)
display(current())
```
- Note: the `ans` output from the last expression is always printed; this is a limitation of interacting with Julia over `stdin`. You can put expressions on the same line separated by semicolons (though the last expression's result will be printed unless you make it `nothing`), and you can also enclose the statements in a `begin ... end` block:
```julia
using Plots
begin
x = range(0, 10, length=100)
y = sin.(x)
plot(x, y)
end
display(current())
```
- Note: you will have to manually load the Plots package the first time (or any other package you use). You may want to put these in a separate block that you only execute on the first use:
```julia
import Pkg
Pkg.add("Plots")
```

</details>

<details>
<summary>C++</summary>

Expand Down Expand Up @@ -757,7 +789,7 @@ print('should not run any pre blocks or global injects')

### Notebook Mode

A few languages (currently JS and Python) support *Notebook Mode*. If a language is using Notebook Mode (configurable in Settings), then all code blocks in a given file will execute in the same environment.
A few languages (currently JS, Python, and Julia) support *Notebook Mode*. If a language is using Notebook Mode (configurable in Settings), then all code blocks in a given file will execute in the same environment.

Variables functions, etc. defined in one code block will be available in other code blocks. Code blocks are executed on demand; the order of code blocks in the file does not affect the order in which they are executed:

Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion src/ExecutorContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import RExecutor from "./executors/RExecutor.js";
import CExecutor from "./executors/CExecutor";
import FSharpExecutor from "./executors/FSharpExecutor";
import LatexExecutor from "./executors/LatexExecutor";
import JuliaExecutor from "./executors/JuliaExecutor";

const interactiveExecutors: Partial<Record<LanguageId, any>> = {
"js": NodeJSExecutor,
"python": PythonExecutor,
"r": RExecutor
"r": RExecutor,
"julia": JuliaExecutor
};

const nonInteractiveExecutors: Partial<Record<LanguageId, any>> = {
Expand Down
1 change: 1 addition & 0 deletions src/RunButton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ async function handleExecution(block: CodeBlockContext) {
case "zig": return runCode(s.zigPath, s.zigArgs, "zig", block, { shell: true });
case "ocaml": return runCode(s.ocamlPath, s.ocamlArgs, "ocaml", block, { shell: true });
case "php": return runCode(s.phpPath, s.phpArgs, s.phpFileExtension, block, { shell: true });
case "julia": return runCode(s.juliaPath, s.juliaArgs,s.juliaFileExtension, block, {transform: (code) => macro.expandJuliaPlot(code,s) });
case "latex":
const outputPath: string = await retrieveFigurePath(block.srcCode, s.latexFigureTitlePattern, block.markdownFile, s);
const invokeCompiler: string = [s.latexTexfotArgs, s.latexCompilerPath, s.latexCompilerArgs].join(" ");
Expand Down
37 changes: 37 additions & 0 deletions src/executors/JuliaExecutor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {ChildProcessWithoutNullStreams} from "child_process";
import {ExecutorSettings} from "src/settings/Settings";
import ReplExecutor from "./ReplExecutor.js";


export default class JuliaExecutor extends ReplExecutor {

process: ChildProcessWithoutNullStreams

constructor(settings: ExecutorSettings, file: string) {
const args = settings.juliaArgs ? settings.juliaArgs.split(" ") : [];

//args.unshift(`--interactive`);

super(settings, settings.juliaPath, args, file, "julia");
}

/**
* Writes a single newline to ensure that the stdin is set up correctly.
*/
async setup() {
this.process.stdin.write("\n");
}

wrapCode(code: string, finishSigil: string): string {

//apparently some issue with Meta.parse: https://github.com/JuliaInterop/JuliaCall/issues/197
//return `try \n eval(Meta.parse(${JSON.stringify(code)}))\n catch err; \n bt = catch_backtrace(); msg = sprint(showerror, err, bt); println(stderr,msg) \n finally \n print(${JSON.stringify(finishSigil)}) \n end\n`
// this works and will return an error if there is one, just not on stderr
return `${code} \n print(${JSON.stringify(finishSigil)}) \n`
}

removePrompts(output: string, source: "stdout" | "stderr"): string {
return output;
}

}
2 changes: 1 addition & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import * as runButton from './RunButton';
export const languageAliases = ["javascript", "typescript", "bash", "csharp", "wolfram", "nb", "wl", "hs", "py", "tex"] as const;
export const canonicalLanguages = ["js", "ts", "cs", "latex", "lean", "lua", "python", "cpp", "prolog", "shell", "groovy", "r",
"go", "rust", "java", "powershell", "kotlin", "mathematica", "haskell", "scala", "swift", "racket", "fsharp", "c", "dart",
"ruby", "batch", "sql", "octave", "maxima", "applescript", "zig", "ocaml", "php"] as const;
"ruby", "batch", "sql", "octave", "maxima", "applescript", "zig", "ocaml", "php", "julia"] as const;
export const supportedLanguages = [...languageAliases, ...canonicalLanguages] as const;
export type LanguageId = typeof canonicalLanguages[number];

Expand Down
12 changes: 12 additions & 0 deletions src/settings/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ export interface ExecutorSettings {
phpArgs: string;
phpFileExtension: string;
phpInject: string;
juliaPath: string;
juliaArgs: string;
juliaFileExtension: string;
juliaEmbedPlots: boolean;
juliaInject: string;
scalaPath: string;
scalaArgs: string;
scalaFileExtension: string;
Expand Down Expand Up @@ -210,6 +215,7 @@ export interface ExecutorSettings {
zigInteractive: boolean;
ocamlInteractive: boolean;
phpInteractive: boolean;
juliaInteractive: boolean;
}


Expand Down Expand Up @@ -387,6 +393,11 @@ export const DEFAULT_SETTINGS: ExecutorSettings = {
phpArgs: "",
phpFileExtension: "php",
phpInject: "",
juliaPath: "julia",
juliaArgs: "",
juliaEmbedPlots: true,
juliaFileExtension: "jl",
juliaInject: "",
jsInteractive: true,
tsInteractive: false,
csInteractive: false,
Expand Down Expand Up @@ -422,4 +433,5 @@ export const DEFAULT_SETTINGS: ExecutorSettings = {
zigInteractive: false,
ocamlInteractive: false,
phpInteractive: false,
juliaInteractive: true,
}
2 changes: 2 additions & 0 deletions src/settings/SettingsTab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import makeGroovySettings from "./per-lang/makeGroovySettings";
import makeHaskellSettings from "./per-lang/makeHaskellSettings";
import makeJavaSettings from "./per-lang/makeJavaSettings";
import makeJsSettings from "./per-lang/makeJsSettings";
import makeJuliaSettings from "./per-lang/makeJuliaSettings";
import makeKotlinSettings from "./per-lang/makeKotlinSettings";
import makeLatexSettings from "./per-lang/makeLatexSettings";
import makeLeanSettings from "./per-lang/makeLeanSettings";
Expand Down Expand Up @@ -172,6 +173,7 @@ export class SettingsTab extends PluginSettingTab {
makeOCamlSettings(this, this.makeContainerFor("ocaml"));
makePhpSettings(this, this.makeContainerFor("php"));
makeLatexSettings(this, this.makeContainerFor("latex"));
makeJuliaSettings(this,this.makeContainerFor("julia"));

this.focusContainer(this.plugin.settings.lastOpenLanguageTab || canonicalLanguages[0]);
}
Expand Down
1 change: 1 addition & 0 deletions src/settings/languageDisplayName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const DISPLAY_NAMES: Record<LanguageId, string> = {
haskell: "Haskell",
java: "Java",
js: "Javascript",
julia: "Julia",
kotlin: "Kotlin",
latex: "LaTeX",
lua: "Lua",
Expand Down
42 changes: 42 additions & 0 deletions src/settings/per-lang/makeJuliaSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Setting } from "obsidian";
import { SettingsTab } from "../SettingsTab";

export default (tab: SettingsTab, containerEl: HTMLElement) => {
containerEl.createEl('h3', { text: 'Julia Settings' });
new Setting(containerEl)
.setName('Embed Julia Plots')
.addToggle(toggle => toggle
.setValue(tab.plugin.settings.juliaEmbedPlots)
.onChange(async (value) => {
tab.plugin.settings.juliaEmbedPlots = value;
await tab.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Julia path')
.addText(text => text
.setValue(tab.plugin.settings.juliaPath)
.onChange(async (value) => {
const sanitized = tab.sanitizePath(value);
tab.plugin.settings.juliaPath = sanitized;
console.log('julia path set to: ' + sanitized);
await tab.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Julia arguments')
.addText(text => text
.setValue(tab.plugin.settings.juliaArgs)
.onChange(async (value) => {
tab.plugin.settings.juliaArgs = value;
console.log('Julia args set to: ' + value);
await tab.plugin.saveSettings();
}));
new Setting(containerEl)
.setName("Run Julia blocks in Notebook Mode")
.addToggle((toggle) => toggle
.setValue(tab.plugin.settings.juliaInteractive)
.onChange(async (value) => {
tab.plugin.settings.juliaInteractive = value;
await tab.plugin.saveSettings();
}));
tab.makeInjectSetting(containerEl, "julia");
}
15 changes: 15 additions & 0 deletions src/transforms/Magic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const PYTHON_PLOT_REGEX = /^(plt|matplotlib.pyplot|pyplot)\.show\(\)/gm;
const R_PLOT_REGEX = /^plot\(.*\)/gm;
const OCTAVE_PLOT_REGEX = /^plot\s*\(.*\);/gm;
const MAXIMA_PLOT_REGEX = /^plot2d\s*\(.*\[.+\]\)\s*[$;]/gm;
const JULIA_PLOT_REGEX = /^display\(current\(\)\)/gm;

/**
* Parses the source code for the @vault command and replaces it with the vault path.
Expand Down Expand Up @@ -291,3 +292,17 @@ export function expandMaximaPlot(source: string): string {
return source;
}


export function expandJuliaPlot(source: string, settings: ExecutorSettings): string {
if (settings.juliaEmbedPlots) {
const matches = source.matchAll(JULIA_PLOT_REGEX); //replacing display(current())
for (const match of matches) {
const tempFile = `${os.tmpdir()}/temp_${Date.now()}.png`.replace(/\\/g, "/").replace(/^\//, "");
const substitute = `savefig("${tempFile}"); println(${JSON.stringify(`${TOGGLE_HTML_SIGIL}<img src="${Platform.resourcePathPrefix + tempFile}" align="center">${TOGGLE_HTML_SIGIL}`)})`;

source = source.replace(match[0], substitute);
}
}
return source;
}