Skip to content

Commit

Permalink
Merge pull request #285 from BetterThanTomorrow/wip/custom-repl-conne…
Browse files Browse the repository at this point in the history
…ct-sequence

Bringing Custom repl connect sequences home to dev!
  • Loading branch information
PEZ authored Sep 10, 2019
2 parents e291b1d + 8792488 commit 78e78b8
Show file tree
Hide file tree
Showing 9 changed files with 1,193 additions and 647 deletions.
598 changes: 265 additions & 333 deletions calva/connector.ts

Large diffs are not rendered by default.

38 changes: 30 additions & 8 deletions calva/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import * as replWindow from "./repl-window";
import { format } from 'url';
import * as greetings from "./greet";
import Analytics from './analytics';
import * as open from 'open';

import { edit } from './paredit/utils';

function onDidSave(document) {
Expand Down Expand Up @@ -63,10 +65,28 @@ function onDidOpen(document) {
function activate(context: vscode.ExtensionContext) {
state.cursor.set('analytics', new Analytics(context));
state.analytics().logPath("/start").logEvent("LifeCycle", "Started").send();

const chan = state.outputChannel();


let legacyExtension = vscode.extensions.getExtension('cospaia.clojure4vscode'),
const legacyExtension = vscode.extensions.getExtension('cospaia.clojure4vscode'),
fmtExtension = vscode.extensions.getExtension('cospaia.calva-fmt'),
pareEditExtension = vscode.extensions.getExtension('cospaia.paredit-revived');
pareEditExtension = vscode.extensions.getExtension('cospaia.paredit-revived'),
customCljsRepl = state.config().customCljsRepl,
replConnectSequences = state.config().replConnectSequences,
BUTTON_GOTO_WIKI = "Open the Wiki",
BUTTON_OK = "Got it",
WIKI_URL = "https://github.com/BetterThanTomorrow/calva/wiki/Custom-Connect-Sequences";

if (customCljsRepl && replConnectSequences.length == 0) {
chan.appendLine("Old customCljsRepl settings detected.");
vscode.window.showErrorMessage("Old customCljsRepl settings detected. You need to specifiy it using the new calva.customConnectSequence setting. See the Calva wiki for instructions.", ...[BUTTON_GOTO_WIKI, BUTTON_OK])
.then(v => {
if (v == BUTTON_GOTO_WIKI) {
open(WIKI_URL);
}
})
}

if (legacyExtension) {
vscode.window.showErrorMessage("Calva Legacy extension detected. Things will break. Please uninstall, or disable, the old Calva extension.", ...["Roger that. Right away!"])
Expand All @@ -87,7 +107,6 @@ function activate(context: vscode.ExtensionContext) {

replWindow.activate(context);

let chan = state.outputChannel();
chan.appendLine("Calva activated.");
let {
lint,
Expand All @@ -109,9 +128,9 @@ function activate(context: vscode.ExtensionContext) {
})
}));
context.subscriptions.push(vscode.commands.registerCommand('calva.jackIn', jackIn.calvaJackIn))
context.subscriptions.push(vscode.commands.registerCommand('calva.connect', connector.connect));
context.subscriptions.push(vscode.commands.registerCommand('calva.connect', connector.connectCommand));
context.subscriptions.push(vscode.commands.registerCommand('calva.toggleCLJCSession', connector.toggleCLJCSession));
context.subscriptions.push(vscode.commands.registerCommand('calva.recreateCljsRepl', connector.recreateCljsRepl));
context.subscriptions.push(vscode.commands.registerCommand('calva.switchCljsBuild', connector.switchCljsBuild));
context.subscriptions.push(vscode.commands.registerCommand('calva.selectCurrentForm', select.selectCurrentForm));
context.subscriptions.push(vscode.commands.registerCommand('calva.loadFile', () => {
EvaluateMiddleWare.loadFile();
Expand Down Expand Up @@ -174,9 +193,12 @@ function activate(context: vscode.ExtensionContext) {
vscode.commands.executeCommand("setContext", "calva:pareditValid", false);
}
status.update();
if (editor && editor.document && editor.document.fileName.match(/\.clj[cs]?/).length && state.config().syncReplNamespaceToCurrentFile) {
replWindow.setREPLNamespace(util.getDocumentNamespace(editor.document))
.catch(reasons => { console.warn(`Namespace sync failed, becauase: ${reasons}`) });
if (editor && editor.document && editor.document.fileName) {
const fileExtIfClj = editor.document.fileName.match(/\.clj[cs]?/);
if (fileExtIfClj && fileExtIfClj.length && state.config().syncReplNamespaceToCurrentFile) {
replWindow.setREPLNamespace(util.getDocumentNamespace(editor.document))
.catch(reasons => { console.warn(`Namespace sync failed, becauase: ${reasons}`) });
}
}
}));
context.subscriptions.push(vscode.workspace.onDidChangeTextDocument(annotations.onDidChangeTextDocument))
Expand Down
229 changes: 229 additions & 0 deletions calva/nrepl/connectSequence.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import * as vscode from "vscode";
import * as state from "../state";
import * as projectTypes from './project-types';
import * as utilities from '../utilities';

enum ProjectTypes {
"Leiningen" = "Leiningen",
"Clojure CLI" = "Clojure CLI",
"shadow-cljs" = "shadow-cljs"
}

enum CljsTypes {
"Figwheel Main" = "Figwheel Main",
"lein-figwheel" = "lein-figwheel",
"shadow-cljs" = "shadow-cljs",
"Nashorn" = "Nashorn",
"User provided" = "User provided"
}

interface CljsTypeConfig {
name: string,
dependsOn?: CljsTypes,
isStarted: boolean,
startCode?: string,
buildsRequired?: boolean,
isReadyToStartRegExp?: string | RegExp,
openUrlRegExp?: string | RegExp,
shouldOpenUrl?: boolean,
connectCode: string | Object,
isConnectedRegExp?: string | RegExp,
printThisLineRegExp?: string | RegExp
}

interface MenuSelecions {
leinProfiles?: string[],
leinAlias?: string,
cljAliases?: string[],
cljsLaunchBuilds?: string[],
cljsDefaultBuild?: string
}

interface ReplConnectSequence {
name: string,
projectType: ProjectTypes,
afterCLJReplJackInCode?: string,
cljsType?: CljsTypes | CljsTypeConfig,
menuSelections?: MenuSelecions,
}

const leiningenDefaults: ReplConnectSequence[] =
[{
name: "Leiningen",
projectType: ProjectTypes.Leiningen
},
{
name: "Leiningen + Figwheel",
projectType: ProjectTypes.Leiningen,
cljsType: CljsTypes["lein-figwheel"]
},
{
name: "Leiningen + Figwheel Main",
projectType: ProjectTypes.Leiningen,
cljsType: CljsTypes["Figwheel Main"]
},
{
name: "Leiningen + Nashorn",
projectType: ProjectTypes.Leiningen,
cljsType: CljsTypes["Nashorn"]
}];

const cljDefaults: ReplConnectSequence[] =
[{
name: "Clojure CLI",
projectType: ProjectTypes["Clojure CLI"]
},
{
name: "Clojure CLI + Figwheel",
projectType: ProjectTypes["Clojure CLI"],
cljsType: CljsTypes["lein-figwheel"]
},
{
name: "Clojure CLI + Figwheel Main",
projectType: ProjectTypes["Clojure CLI"],
cljsType: CljsTypes["Figwheel Main"]
},
{
name: "Clojure CLI + Nashorn",
projectType: ProjectTypes["Clojure CLI"],
cljsType: CljsTypes["Nashorn"]
}];

const shadowCljsDefaults: ReplConnectSequence[] = [{
name: "shadow-cljs",
projectType: ProjectTypes["shadow-cljs"],
cljsType: CljsTypes["shadow-cljs"]
}]

const defaultSequences = {
"lein": leiningenDefaults,
"clj": cljDefaults,
"shadow-cljs": shadowCljsDefaults
};

const defaultCljsTypes: { [id: string]: CljsTypeConfig } = {
"Figwheel Main": {
name: "Figwheel Main",
buildsRequired: true,
isStarted: false,
startCode: `(do (require 'figwheel.main.api) (figwheel.main.api/start %BUILDS%))`,
isReadyToStartRegExp: /Prompt will show|Open(ing)? URL|already running/,
openUrlRegExp: /(Starting Server at|Open(ing)? URL) (?<url>\S+)/,
shouldOpenUrl: false,
connectCode: `(do (use 'figwheel.main.api) (figwheel.main.api/cljs-repl %BUILD%))`,
isConnectedRegExp: /To quit, type: :cljs\/quit/
},
"lein-figwheel": {
name: "lein-figwheel",
buildsRequired: false,
isStarted: false,
isReadyToStartRegExp: /Launching ClojureScript REPL for build/,
openUrlRegExp: /Figwheel: Starting server at (?<url>\S+)/,
// shouldOpenUrl: will be set at use-time of this config,
connectCode: "(do (use 'figwheel-sidecar.repl-api) (if (not (figwheel-sidecar.repl-api/figwheel-running?)) (figwheel-sidecar.repl-api/start-figwheel!)) (figwheel-sidecar.repl-api/cljs-repl))",
isConnectedRegExp: /To quit, type: :cljs\/quit/
},
"shadow-cljs": {
name: "shadow-cljs",
buildsRequired: true,
isStarted: true,
// isReadyToStartRegExp: /To quit, type: :cljs\/quit/,
connectCode: {
build: `(shadow.cljs.devtools.api/nrepl-select %BUILD%)`,
repl: `(shadow.cljs.devtools.api/%REPL%)`
},
shouldOpenUrl: false,
isConnectedRegExp: /:selected/
},
"Nashorn": {
name: "Nashorn",
buildsRequired: false,
isStarted: true,
connectCode: "(do (require 'cljs.repl.nashorn) (cider.piggieback/cljs-repl (cljs.repl.nashorn/repl-env)))",
isConnectedRegExp: "To quit, type: :cljs/quit"
}
};

/** Retrieve the replConnectSequences from the config */
function getCustomConnectSequences(): ReplConnectSequence[] {
let sequences: ReplConnectSequence[] = state.config().replConnectSequences;

for (let sequence of sequences) {
if (sequence.name == undefined ||
sequence.projectType == undefined) {

vscode.window.showWarningMessage("Check your calva.replConnectSequences. " +
"You need to supply name and projectType for every sequence. " +
"After fixing the customSequences can be used.");

return [];
}
}

return sequences;
}

/**
* Retrieve the replConnectSequences and returns only that if only one was defined.
* Otherwise the user defined will be combined with the defaults one to be returned.
* @param projectType what default Sequences would be used (leiningen, clj, shadow-cljs)
*/
function getConnectSequences(projectTypes: string[]): ReplConnectSequence[] {
let customSequences = getCustomConnectSequences();

if (customSequences.length) {
return customSequences;
} else {
let result = [];
for (let pType of projectTypes) {
result = result.concat(defaultSequences[pType]);
}
return result;
}
}

/**
* Returns the CLJS-Type description of one of the build-in.
* @param cljsType Build-in cljsType
*/
function getDefaultCljsType(cljsType: string): CljsTypeConfig {
// TODO: Find a less hacky way to get dynamic config for lein-figwheel
defaultCljsTypes["lein-figwheel"].shouldOpenUrl = state.config().openBrowserWhenFigwheelStarted;
return defaultCljsTypes[cljsType];
}

async function askForConnectSequence(cljTypes: string[], saveAs: string, logLabel: string): Promise<ReplConnectSequence> {
// figure out what possible kinds of project we're in
if (cljTypes.length == 0) {
vscode.window.showErrorMessage("Cannot find project, no project.clj, deps.edn or shadow-cljs.edn.");
state.analytics().logEvent("REPL", logLabel, "FailedFindingProjectType").send();
return;
}

const sequences = getConnectSequences(cljTypes);
if (sequences.length > 1) {
const projectConnectSequenceName = await utilities.quickPickSingle({
values: sequences.map(s => { return s.name }),
placeHolder: "Please select a project type",
saveAs: `${state.getProjectRoot()}/${saveAs}`,
autoSelect: true
});
if (!projectConnectSequenceName) {
state.analytics().logEvent("REPL", logLabel, "NoProjectTypePicked").send();
return;
}

return sequences.find(seq => seq.name === projectConnectSequenceName);
} else {
return sequences[0];
}
}

export {
askForConnectSequence,
getConnectSequences,
getDefaultCljsType,
CljsTypes,
ReplConnectSequence,
CljsTypeConfig
}
Loading

0 comments on commit 78e78b8

Please sign in to comment.