Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use cljs for state #1053

Merged
merged 30 commits into from
Mar 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f87097b
Add state ns and add exports to shadow build
bpringe Mar 3, 2021
d01b2e5
Remove unusued import
bpringe Mar 3, 2021
171d5cf
Replace all setting of state values to cljs state api
bpringe Mar 3, 2021
29f529d
Replace getting of state values with new cljs state api
bpringe Mar 3, 2021
efe3ff6
Remove unused imports and consolidate others
bpringe Mar 3, 2021
a1fc05c
Clean up state.ts and set initial state in cljs state
bpringe Mar 4, 2021
e475bdc
Add namespace2 duplicate for testing
bpringe Mar 5, 2021
fab6376
Update namespace2 and test code
bpringe Mar 5, 2021
ee6c427
Clean up namespace2
bpringe Mar 5, 2021
ccb5837
Comment call to namespace2
bpringe Mar 5, 2021
cabb67e
Change updateREPLSessionType to getReplSessionType, leaving state rea…
bpringe Mar 5, 2021
8891a18
Add updateReplSessionType function to utilities module.
bpringe Mar 5, 2021
1740fea
Update changelog.
bpringe Mar 5, 2021
512728c
Refactor config module to use separately declared constants
bpringe Mar 8, 2021
63b4ba0
Move documentSelector from state to config
bpringe Mar 8, 2021
2934a2a
Move workspace config function into config module
bpringe Mar 8, 2021
7720f36
Move session functions out of namespace and into utilities module
bpringe Mar 8, 2021
2353151
Export cljsLib from utilities to fix dev issue
bpringe Mar 8, 2021
c3cf657
Remove unused imports
bpringe Mar 8, 2021
5eb5133
Use cljsLib proxy for remaining cljsLib call in namespace.ts
bpringe Mar 9, 2021
6975922
Move session functions into separate module
bpringe Mar 9, 2021
fa32766
Removed unused import
bpringe Mar 10, 2021
03673de
Merge remote-tracking branch 'origin/dev' into use_cljs_for_state
bpringe Mar 10, 2021
a8c5193
Update CHANGELOG.md
bpringe Mar 10, 2021
dce599c
Change config imports and usages to use config alias for property access
bpringe Mar 10, 2021
33f8e72
Merge branch 'use_cljs_for_state' of github.com:BetterThanTomorrow/ca…
bpringe Mar 10, 2021
b8b9455
Move repl-session module to nrepl directory
bpringe Mar 10, 2021
e317dd3
Add tests for calva.state namespace
bpringe Mar 10, 2021
b565c04
Fix test name
bpringe Mar 10, 2021
2a20e8e
Change getWorkspaceConfig to getConfig
bpringe Mar 10, 2021
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Changes to Calva.

## [Unreleased]
- Implementation detail: [Use cljs for state](https://github.com/BetterThanTomorrow/calva/pull/1053)

## [2.0.178] - 2021-03-09
- [Add command for evaluating from start of list to cursor](https://github.com/BetterThanTomorrow/calva/issues/1057)
Expand Down
18,907 changes: 17,952 additions & 955 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion shadow-cljs.edn
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@
:deactivateLsp calva.lsp/deactivate
:getReferences calva.lsp/get-references
:getDocumentSymbols calva.lsp/get-document-symbols
:LSP_CLIENT_KEY calva.lsp/client-key}
:LSP_CLIENT_KEY calva.lsp/client-key
:setStateValue calva.state/set-state-value!
:getStateValue calva.state/get-state-value
:getState calva.state/get-state
:removeStateValue calva.state/remove-state-value!}
:output-to "out/cljs-lib/cljs-lib.js"}
:test
{:target :node-test
Expand Down
9 changes: 4 additions & 5 deletions src/calva-fmt/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import * as vscode from 'vscode';
import * as state from '../../state';
import { FormatOnTypeEditProvider } from './providers/ontype_formatter';
import { RangeEditProvider } from './providers/range_formatter';
import * as formatter from './format';
import * as inferer from './infer';
import * as docmirror from "../../doc-mirror/index"
import * as config from './config'
import * as calvaConfig from '../../config';

function getLanguageConfiguration(autoIndentOn: boolean): vscode.LanguageConfiguration {
return {
onEnterRules: autoIndentOn && state.config().format ? [
onEnterRules: autoIndentOn && calvaConfig.getConfig().format ? [
// When Calva is the formatter disable all vscode default indentation
// (By outdenting a lot, which is the only way I have found that works)
// TODO: Make it actually consider whether Calva is the formatter or not
Expand All @@ -24,7 +24,6 @@ function getLanguageConfiguration(autoIndentOn: boolean): vscode.LanguageConfigu
}
}


export function activate(context: vscode.ExtensionContext) {
docmirror.activate();
vscode.languages.setLanguageConfiguration("clojure", getLanguageConfiguration(config.getConfig()["format-as-you-type"]));
Expand All @@ -33,8 +32,8 @@ export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.commands.registerTextEditorCommand('calva-fmt.inferParens', inferer.inferParensCommand));
context.subscriptions.push(vscode.commands.registerTextEditorCommand('calva-fmt.tabIndent', (e) => { inferer.indentCommand(e, " ", true) }));
context.subscriptions.push(vscode.commands.registerTextEditorCommand('calva-fmt.tabDedent', (e) => { inferer.indentCommand(e, " ", false) }));
context.subscriptions.push(vscode.languages.registerOnTypeFormattingEditProvider(state.documentSelector, new FormatOnTypeEditProvider, "\r", "\n"));
context.subscriptions.push(vscode.languages.registerDocumentRangeFormattingEditProvider(state.documentSelector, new RangeEditProvider));
context.subscriptions.push(vscode.languages.registerOnTypeFormattingEditProvider(calvaConfig.documentSelector, new FormatOnTypeEditProvider, "\r", "\n"));
context.subscriptions.push(vscode.languages.registerDocumentRangeFormattingEditProvider(calvaConfig.documentSelector, new RangeEditProvider));
vscode.window.onDidChangeActiveTextEditor(inferer.updateState);
vscode.workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration("calva.fmt.formatAsYouType")) {
Expand Down
20 changes: 20 additions & 0 deletions src/cljs-lib/src/calva/state.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
(ns calva.state)

(defonce ^:private state (atom {}))

(defn set-state-value! [key value]
(swap! state assoc key value))

(defn remove-state-value! [key]
(swap! state dissoc key))

(defn get-state-value [key]
(get @state key))

(defn get-state []
@state)

(comment
(set-state-value! "hello" "world")
(get-state)
(remove-state-value! "hello"))
31 changes: 31 additions & 0 deletions src/cljs-lib/test/calva/state_test.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
(ns calva.state-test
(:require [cljs.test :refer [testing deftest is use-fixtures]]
[calva.state :as state]))

(use-fixtures :each
{:before (fn [] (reset! state/state {}))})

(deftest set-state-value!-test
(testing "Should write value to state, given key"
(state/set-state-value! "hello" "world")
(is (= {"hello" "world"} @state/state))))

(deftest remove-state-value!-test
(testing "Should remove value from state, given key"
(reset! state/state {"hello" "world"})
(state/remove-state-value! "hello")
(is (= {} @state/state))))

(deftest get-state-value-test
(testing "Should get value from state, given key"
(reset! state/state {"hello" "world"})
(is (= "world" (state/get-state-value "hello")))))

(deftest get-state-test
(testing "Should return all state"
(let [all-state {"hello" "world" "fizz" "buzz"}]
(reset! state/state all-state)
(is (= all-state (state/get-state))))))

(comment
(state/get-state))
71 changes: 63 additions & 8 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,68 @@
const config = {
REPL_FILE_EXT: 'calva-repl',
KEYBINDINGS_ENABLED_CONFIG_KEY: 'calva.keybindingsEnabled',
KEYBINDINGS_ENABLED_CONTEXT_KEY: 'calva:keybindingsEnabled'
};
import * as vscode from 'vscode';
import { customREPLCommandSnippet } from './evaluate';
import { ReplConnectSequence } from './nrepl/connectSequence';
import { PrettyPrintingOptions } from './printer';

const REPL_FILE_EXT = 'calva-repl';
const KEYBINDINGS_ENABLED_CONFIG_KEY = 'calva.keybindingsEnabled';
const KEYBINDINGS_ENABLED_CONTEXT_KEY = 'calva:keybindingsEnabled';

type ReplSessionType = 'clj' | 'cljs';

export {
ReplSessionType
// include the 'file' and 'untitled' to the
// document selector. All other schemes are
// not known and therefore not supported.
const documentSelector = [
{ scheme: 'file', language: 'clojure' },
{ scheme: 'jar', language: 'clojure' },
{ scheme: 'untitled', language: 'clojure' }
];

/**
* Trims EDN alias and profile names from any surrounding whitespace or `:` characters.
* This in order to free the user from having to figure out how the name should be entered.
* @param {string} name
* @return {string} The trimmed name
*/
function _trimAliasName(name: string): string {
return name.replace(/^[\s,:]*/, "").replace(/[\s,:]*$/, "")
}

export default config;
// TODO find a way to validate the configs
function getConfig() {
const configOptions = vscode.workspace.getConfiguration('calva');
return {
format: configOptions.get("formatOnSave"),
evaluate: configOptions.get("evalOnSave"),
test: configOptions.get("testOnSave"),
showDocstringInParameterHelp: configOptions.get("showDocstringInParameterHelp") as boolean,
jackInEnv: configOptions.get("jackInEnv"),
jackInDependencyVersions: configOptions.get("jackInDependencyVersions") as { JackInDependency: string },
openBrowserWhenFigwheelStarted: configOptions.get("openBrowserWhenFigwheelStarted") as boolean,
customCljsRepl: configOptions.get("customCljsRepl", null),
replConnectSequences: configOptions.get("replConnectSequences") as ReplConnectSequence[],
myLeinProfiles: configOptions.get("myLeinProfiles", []).map(_trimAliasName) as string[],
myCljAliases: configOptions.get("myCljAliases", []).map(_trimAliasName) as string[],
asyncOutputDestination: configOptions.get("sendAsyncOutputTo") as string,
customREPLCommandSnippets: configOptions.get("customREPLCommandSnippets", []),
customREPLCommandSnippetsGlobal: configOptions.inspect("customREPLCommandSnippets").globalValue as customREPLCommandSnippet[],
customREPLCommandSnippetsWorkspace: configOptions.inspect("customREPLCommandSnippets").workspaceValue as customREPLCommandSnippet[],
customREPLCommandSnippetsWorkspaceFolder: configOptions.inspect("customREPLCommandSnippets").workspaceFolderValue as customREPLCommandSnippet[],
prettyPrintingOptions: configOptions.get("prettyPrintingOptions") as PrettyPrintingOptions,
enableJSCompletions: configOptions.get("enableJSCompletions") as boolean,
autoOpenREPLWindow: configOptions.get("autoOpenREPLWindow") as boolean,
autoOpenJackInTerminal: configOptions.get("autoOpenJackInTerminal") as boolean,
referencesCodeLensEnabled: configOptions.get('referencesCodeLens.enabled') as boolean,
displayDiagnostics: configOptions.get('displayDiagnostics') as boolean,
hideReplUi: configOptions.get('hideReplUi') as boolean
};
}

export {
REPL_FILE_EXT,
KEYBINDINGS_ENABLED_CONFIG_KEY,
KEYBINDINGS_ENABLED_CONTEXT_KEY,
documentSelector,
ReplSessionType,
getConfig
}
61 changes: 31 additions & 30 deletions src/connector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import * as open from 'open';
import status from './status';
import * as projectTypes from './nrepl/project-types';
import { NReplClient, NReplSession } from "./nrepl";
import { CljsTypeConfig, ReplConnectSequence, getDefaultCljsType, CljsTypes, askForConnectSequence } from './nrepl/connectSequence';
import { CljsTypeConfig, ReplConnectSequence, getDefaultCljsType, askForConnectSequence } from './nrepl/connectSequence';
import { disabledPrettyPrinter } from './printer';
import { keywordize } from './util/string';
import { REQUESTS, initializeDebugger } from './debugger/calva-debug';
import { initializeDebugger } from './debugger/calva-debug';
import * as outputWindow from './results-output/results-doc';
import evaluate from './evaluate';
import * as namespace from './namespace';
import * as liveShareSupport from './liveShareSupport';
import * as calvaDebug from './debugger/calva-debug';
import { setStateValue, getStateValue } from '../out/cljs-lib/cljs-lib';
import * as replSession from './nrepl/repl-session';

async function connectToHost(hostname: string, port: number, connectSequence: ReplConnectSequence) {
state.analytics().logEvent("REPL", "Connecting").send();
Expand Down Expand Up @@ -51,11 +52,11 @@ async function connectToHost(hostname: string, port: number, connectSequence: Re
util.setConnectingState(false);
util.setConnectedState(true);
state.analytics().logEvent("REPL", "ConnectedCLJ").send();
state.cursor.set('clj', cljSession);
state.cursor.set('cljc', cljSession);
setStateValue('clj', cljSession);
setStateValue('cljc', cljSession);
status.update();
outputWindow.append(`; Connected session: clj\n${outputWindow.CLJ_CONNECT_GREETINGS}`);
namespace.updateREPLSessionType();
replSession.updateReplSessionType();

await initializeDebugger(cljSession);

Expand Down Expand Up @@ -91,6 +92,7 @@ async function connectToHost(hostname: string, port: number, connectSequence: Re
util.setConnectedState(false);
outputWindow.append("; Failed connecting.");
state.analytics().logEvent("REPL", "FailedConnectingCLJ").send();
console.error('Failed connecting:', e);
return false;
}

Expand All @@ -100,11 +102,11 @@ async function connectToHost(hostname: string, port: number, connectSequence: Re
}

async function setUpCljsRepl(session, build) {
state.cursor.set("cljs", session);
setStateValue("cljs", session);
status.update();
outputWindow.append(`; Connected session: cljs${(build ? ", repl: " + build : "")}\n${outputWindow.CLJS_CONNECT_GREETINGS}`);
outputWindow.setSession(session, 'cljs.user');
namespace.updateREPLSessionType();
replSession.updateReplSessionType();
}

async function getFigwheelMainBuilds() {
Expand Down Expand Up @@ -156,7 +158,7 @@ async function evalConnectCode(newCljsSession: NReplSession, code: string, name:
});
if (checkSuccess(valueResult, out, err)) {
state.analytics().logEvent("REPL", "ConnectedCLJS", name).send();
state.cursor.set('cljs', cljsSession = newCljsSession)
setStateValue('cljs', cljsSession = newCljsSession)
return true
} else {
return false;
Expand Down Expand Up @@ -291,7 +293,7 @@ function createCLJSReplType(cljsType: CljsTypeConfig, cljsTypeName: string, conn
return;
}

state.cursor.set('cljsBuild', build);
setStateValue('cljsBuild', build);

return evalConnectCode(session, initCode, name, checkFn, [startAppNowProcessor, printThisPrinter], [allPrinter]);
},
Expand Down Expand Up @@ -379,20 +381,20 @@ async function makeCljsSessionClone(session, repl: ReplType, projectTypeName: st
} else {
state.analytics().logEvent("REPL", "FailedStartingCLJS", repl.name).send();
outputWindow.append("; Failed starting cljs repl");
state.cursor.set('cljsBuild', null);
setStateValue('cljsBuild', null);
return [null, null];
}
}
if (await repl.connect(newCljsSession, repl.name, repl.connected)) {
state.analytics().logEvent("REPL", "ConnectedCLJS", repl.name).send();
state.cursor.set('cljs', cljsSession = newCljsSession);
return [cljsSession, state.deref().get('cljsBuild')];
setStateValue('cljs', cljsSession = newCljsSession);
return [cljsSession, getStateValue('cljsBuild')];
} else {
let build = state.deref().get('cljsBuild')
let build = getStateValue('cljsBuild')
state.analytics().logEvent("REPL", "FailedConnectingCLJS", repl.name).send();
let failed = "Failed starting cljs repl" + (build != null ? ` for build: ${build}. Is the build running and connected?\n See the Output channel "Calva Connection Log" for any hints on what went wrong.` : "");
outputWindow.append(`; ${failed}`);
state.cursor.set('cljsBuild', null);
setStateValue('cljsBuild', null);
vscode.window.showInformationMessage(
failed,
{ modal: true },
Expand All @@ -419,8 +421,8 @@ async function promptForNreplUrlAndConnect(port, connectSequence: ReplConnectSeq
let [hostname, port] = url.split(':'),
parsedPort = parseFloat(port);
if (parsedPort && parsedPort > 0 && parsedPort < 65536) {
state.cursor.set("hostname", hostname);
state.cursor.set("port", parsedPort);
setStateValue("hostname", hostname);
setStateValue("port", parsedPort);
await connectToHost(hostname, parsedPort, connectSequence);
} else {
outputWindow.append("; Bad url: " + url);
Expand Down Expand Up @@ -463,8 +465,8 @@ export async function connect(connectSequence: ReplConnectSequence,
if (port) {
hostname = hostname !== undefined ? hostname : "localhost";
if (isAutoConnect) {
state.cursor.set("hostname", hostname);
state.cursor.set("port", port);
setStateValue("hostname", hostname);
setStateValue("port", port);
await connectToHost(hostname, parseInt(port), connectSequence);
} else {
await promptForNreplUrlAndConnect(port, connectSequence);
Expand Down Expand Up @@ -526,10 +528,10 @@ export default {
disconnect: (options = null, callback = () => { }) => {
status.updateNeedReplUi(false);
['clj', 'cljs'].forEach(sessionType => {
state.cursor.set(sessionType, null);
setStateValue(sessionType, null);
});
util.setConnectedState(false);
state.cursor.set('cljc', null);
setStateValue('cljc', null);
status.update();

if (nClient) {
Expand All @@ -547,26 +549,25 @@ export default {
callback();
},
toggleCLJCSession: () => {
let current = state.deref();
let newSession: NReplSession;

if (current.get('connected')) {
if (namespace.getSession('cljc') == namespace.getSession('cljs')) {
newSession = namespace.getSession('clj');
} else if (namespace.getSession('cljc') == namespace.getSession('clj')) {
newSession = namespace.getSession('cljs');
if (getStateValue('connected')) {
if (replSession.getSession('cljc') == replSession.getSession('cljs')) {
newSession = replSession.getSession('clj');
} else if (replSession.getSession('cljc') == replSession.getSession('clj')) {
newSession = replSession.getSession('cljs');
}
state.cursor.set('cljc', newSession);
setStateValue('cljc', newSession);
if (outputWindow.isResultsDoc(vscode.window.activeTextEditor.document)) {
outputWindow.setSession(newSession, undefined);
namespace.updateREPLSessionType();
replSession.updateReplSessionType();
outputWindow.appendPrompt();
}
status.update();
}
},
switchCljsBuild: async () => {
let cljSession = namespace.getSession('clj');
let cljSession = replSession.getSession('clj');
const cljsTypeName: string = state.extensionContext.workspaceState.get('selectedCljsTypeName'),
cljTypeName: string = state.extensionContext.workspaceState.get('selectedCljTypeName');
state.analytics().logEvent("REPL", "switchCljsBuild", cljsTypeName).send();
Expand Down
Loading