Skip to content

Commit 8fbcbfc

Browse files
committed
auto open in default or specified viewer
1 parent 20fb64c commit 8fbcbfc

File tree

3 files changed

+138
-13
lines changed

3 files changed

+138
-13
lines changed

apps/vscode/package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,16 @@
10361036
"default": true,
10371037
"markdownDescription": "When using a venv or conda environment, prefer Quarto CLI installed with pip in that environment. This will override Quarto CLI in the `PATH`, but not an explicitly configured `#quarto.path#`."
10381038
},
1039+
"quarto.defaultEditor": {
1040+
"order": 11,
1041+
"type": "string",
1042+
"markdownDescription": "Default editor",
1043+
"enum": [
1044+
"source",
1045+
"visual"
1046+
],
1047+
"default": "source"
1048+
},
10391049
"quarto.render.renderOnSave": {
10401050
"order": 12,
10411051
"scope": "window",

apps/vscode/src/main.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,20 @@ import { activateStatusBar } from "./providers/statusbar";
2929
import { walkthroughCommands } from "./providers/walkthrough";
3030
import { activateLuaTypes } from "./providers/lua-types";
3131
import { activateCreate } from "./providers/create/create";
32-
import { activateEditor } from "./providers/editor/editor";
32+
import { activateEditor, VisualEditorProvider } from "./providers/editor/editor";
3333
import { activateCopyFiles } from "./providers/copyfiles";
3434
import { activateZotero } from "./providers/zotero/zotero";;
3535
import { extensionHost } from "./host";
3636
import { configuredQuartoPath } from "./core/quarto";
3737
import { activateDenoConfig } from "./providers/deno-config";
38+
import { determineMode, setEditorOpener } from "./providers/editor/toggle";
39+
import { URI } from "vscode-languageserver-types";
40+
import { config } from "vscode-nls";
41+
42+
let suppressOpenHandler = false;
3843

3944
export async function activate(context: vscode.ExtensionContext) {
40-
45+
4146
// create extension host
4247
const host = extensionHost();
4348

@@ -125,9 +130,36 @@ export async function activate(context: vscode.ExtensionContext) {
125130

126131
// activate providers common to browser/node
127132
activateCommon(context, host, engine, commands);
133+
134+
// if positron
135+
setEditorOpener();
136+
137+
vscode.workspace.onDidChangeConfiguration(async (event) => {
138+
if (event.affectsConfiguration('quarto.defaultEditor')) {
139+
setEditorOpener();
140+
}
141+
});
142+
// end if positron
143+
144+
const documentOpenHandler = vscode.workspace.onDidOpenTextDocument(async (document: vscode.TextDocument) => {
145+
// Check if the document language is "quarto"
146+
const config = vscode.workspace.getConfiguration('quarto').get<string>('defaultEditor');
147+
if (suppressOpenHandler) {
148+
suppressOpenHandler = false; // Reset the flag
149+
return; // Prevent further execution
150+
}
151+
if (document.languageId === 'quarto') {
152+
suppressOpenHandler = await determineMode(document, config);
153+
154+
}
155+
});
156+
157+
// Add the event handler to the context subscriptions
158+
context.subscriptions.push(documentOpenHandler);
159+
128160
}
129161

130162
export async function deactivate() {
131163
return deactivateLsp();
132-
}
164+
}
133165

apps/vscode/src/providers/editor/toggle.ts

Lines changed: 93 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,99 @@
1212
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
1313
*
1414
*/
15-
15+
import * as vscode from "vscode";
1616
import { commands, window, workspace, TextDocument, ViewColumn } from "vscode";
1717
import { Command } from "../../core/command";
1818
import { isQuartoDoc, kQuartoLanguageId } from "../../core/doc";
19+
import * as quarto from "quarto-core";
20+
import fs from "node:fs";
21+
import * as path from 'path';
22+
import yaml from "js-yaml";
1923
import { VisualEditorProvider } from "./editor";
2024

2125

26+
export async function determineMode(doc: TextDocument, config: string | undefined): Promise<boolean> {
27+
const text = doc.getText()
28+
29+
let editorOpener = undefined;
30+
31+
// check if file itself has a mode
32+
if (hasEditorMode(text, "source")) {
33+
editorOpener = "source";
34+
}
35+
else if (hasEditorMode(text, "visual")) {
36+
editorOpener = "visual";
37+
}
38+
// check if has a _quarto.yml or _quarto.yaml file with editor specified
39+
else {
40+
editorOpener = workspaceHasQuartoYaml();
41+
}
42+
if (editorOpener && editorOpener != config) {
43+
editorOpener = editorOpener === 'visual' ? VisualEditorProvider.viewType : 'textEditor';
44+
await vscode.commands.executeCommand('workbench.action.closeActiveEditor');
45+
await vscode.commands.executeCommand("vscode.openWith",
46+
doc.uri,
47+
editorOpener
48+
);
49+
return true;
50+
}
51+
52+
return false;
53+
}
54+
55+
export async function setEditorOpener() {
56+
const config = vscode.workspace.getConfiguration('quarto').get<string>('defaultEditor');
57+
const viewType = config === 'visual' ? VisualEditorProvider.viewType : 'textEditor';
58+
vscode.workspace.getConfiguration('workbench').update('editor.defaultView', viewType, true);
59+
await vscode.commands.executeCommand("workbench.action.setDefaultEditor",
60+
vscode.Uri.file('filename.qmd'),
61+
viewType
62+
);
63+
}
64+
65+
export function workspaceHasQuartoYaml() {
66+
const workspaceFolders = vscode.workspace.workspaceFolders;
67+
68+
if (workspaceFolders && workspaceFolders.length > 0) {
69+
const rootPath = workspaceFolders[0].uri.fsPath; // Only look in the root directory of the first workspace folder
2270

71+
const quartoFilePathYml = path.join(rootPath, '_quarto.yml');
72+
const quartoFilePathYaml = path.join(rootPath, '_quarto.yaml');
2373

24-
export function editInVisualModeCommand() : Command {
74+
let fileContent: string | null = null;
75+
76+
if (fs.existsSync(quartoFilePathYml)) {
77+
fileContent = fs.readFileSync(quartoFilePathYml, 'utf8');
78+
} else if (fs.existsSync(quartoFilePathYaml)) {
79+
fileContent = fs.readFileSync(quartoFilePathYaml, 'utf8');
80+
}
81+
82+
if (fileContent) {
83+
const parsedYaml = yaml.load(fileContent) as any;
84+
if (parsedYaml.editor === 'visual' || parsedYaml.editor === 'source') {
85+
return parsedYaml.editor;
86+
}
87+
}
88+
}
89+
90+
return undefined;
91+
}
92+
93+
export function hasEditorMode(doc: string, mode: string) {
94+
if (doc) {
95+
const match = doc.match(quarto.kRegExYAML);
96+
if (match) {
97+
const yaml = match[0];
98+
return (
99+
!!yaml.match(new RegExp("^editor:\\s+" + mode + "\\s*$", "gm")) ||
100+
!!yaml.match(new RegExp("^[ \\t]*" + mode + ":\\s*(default)?\\s*$", "gm"))
101+
);
102+
}
103+
}
104+
return false;
105+
}
106+
107+
export function editInVisualModeCommand(): Command {
25108
return {
26109
id: "quarto.editInVisualMode",
27110
execute() {
@@ -33,14 +116,14 @@ export function editInVisualModeCommand() : Command {
33116
};
34117
}
35118

36-
export function editInSourceModeCommand() : Command {
119+
export function editInSourceModeCommand(): Command {
37120
return {
38121
id: "quarto.editInSourceMode",
39122
execute() {
40123
const activeVisual = VisualEditorProvider.activeEditor();
41124
if (activeVisual) {
42125
reopenEditorInSourceMode(activeVisual.document, '', activeVisual.viewColumn);
43-
}
126+
}
44127
}
45128
};
46129
}
@@ -49,14 +132,14 @@ export async function reopenEditorInVisualMode(
49132
document: TextDocument,
50133
viewColumn?: ViewColumn
51134
) {
52-
135+
53136
// save then close
54137
await commands.executeCommand("workbench.action.files.save");
55138
await commands.executeCommand('workbench.action.closeActiveEditor');
56139

57140
// open in visual mode
58-
await commands.executeCommand("vscode.openWith",
59-
document.uri,
141+
await commands.executeCommand("vscode.openWith",
142+
document.uri,
60143
VisualEditorProvider.viewType,
61144
{
62145
viewColumn
@@ -65,8 +148,8 @@ export async function reopenEditorInVisualMode(
65148
}
66149

67150
export async function reopenEditorInSourceMode(
68-
document: TextDocument,
69-
untitledContent?: string,
151+
document: TextDocument,
152+
untitledContent?: string,
70153
viewColumn?: ViewColumn
71154
) {
72155
if (!document.isUntitled) {
@@ -91,5 +174,5 @@ export async function reopenEditorInSourceMode(
91174
await window.showTextDocument(doc, viewColumn, false);
92175
}
93176
});
94-
177+
95178
}

0 commit comments

Comments
 (0)