From 6393324e4992ca006e8851a6c62bdf991f0e6827 Mon Sep 17 00:00:00 2001 From: Luma Date: Thu, 4 Mar 2021 20:16:28 +0900 Subject: [PATCH] feat: lessen restarts, stabilize, and more opinionated defaults (#2) Lessen restarts: - Won't restart if there is already workspace configuration and configured properly. - Won't write configuration if runtime target is node. This can be configured by `tsdetect.doNotCreateOnNode`. - Won't overwrite configuration if there already exists file by default. This can be configured by `tsdetect.doNothingIfConfigExists`. - Overriding more options can be set by `tsdetect.{deno,node}Override` BREAKING CHANGE: Only left workspace configuration target. --- autoload/tsdetect/coc.vim | 4 +- autoload/tsdetect/coc/auto.vim | 57 ++++++-------- package.json | 134 +++++++++------------------------ src/coc_internal.ts | 5 ++ src/commands.ts | 87 +++++++++++---------- src/entrypoints/coc.ts | 33 +++----- src/globals.d.ts | 9 +++ src/set_config.ts | 31 -------- src/settings.ts | 23 +++--- 9 files changed, 143 insertions(+), 240 deletions(-) create mode 100644 src/coc_internal.ts create mode 100644 src/globals.d.ts diff --git a/autoload/tsdetect/coc.vim b/autoload/tsdetect/coc.vim index ede740a..fe9df9b 100644 --- a/autoload/tsdetect/coc.vim +++ b/autoload/tsdetect/coc.vim @@ -1,8 +1,8 @@ -function! tsdetect#coc#setup_switch(mode, config_type) abort +function! tsdetect#coc#setup_switch(mode) abort augroup tsdetect#coc#setup_switch autocmd! if a:mode == 'auto' - execute printf("autocmd User tsdetect#detect ++nested call tsdetect#coc#auto#switch_%s()", a:config_type) + autocmd User tsdetect#detect ++nested if get(g:, 'coc_enabled', 0) | call tsdetect#coc#auto#switch() | endif endif augroup END endfunction diff --git a/autoload/tsdetect/coc/auto.vim b/autoload/tsdetect/coc/auto.vim index 40f1a09..6d540b1 100644 --- a/autoload/tsdetect/coc/auto.vim +++ b/autoload/tsdetect/coc/auto.vim @@ -1,41 +1,28 @@ -let s:ephemeral_did_configured = 0 - -let s:ephemeral_condition = "if s:ephemeral_did_configured" -let s:permanent_condition = "if get(g:, 'tsdetect#coc#auto#switched_%s_%s', 0)" - -for [s:config_type, s:condition] in [ - \ ["ephemeral", s:ephemeral_condition], - \ ["workspace", s:permanent_condition], - \ ["user", s:permanent_condition], - \ ] - for [s:node, s:deno] in [["node", "deno"], ["deno", "node"]] - execute join([ - \ printf("function! tsdetect#coc#auto#switch_%s_%s() abort", s:config_type, s:node), - \ printf(" doautocmd User tsdetect#coc#auto#swtich#%s#%s#before", s:config_type, s:node), - \ s:config_type == 'ephemeral' ? "let s:ephemeral_did_configured = 1" : "", - \ printf(" let g:tsdetect#coc#auto#switched_%s_%s = 1", s:config_type, s:node), - \ printf(" let g:tsdetect#coc#auto#switched_%s_%s = 0", s:config_type, s:deno), - \ printf(" CocCommand tsdetect.internal.%s.%s.initializeWorkspace", s:config_type, s:node), - \ printf("endfunction"), - \ printf(""), - \ printf("function! tsdetect#coc#auto#switch_%s_%s_if_necessary() abort", s:config_type, s:node), - \ s:config_type == 'ephemeral' ? s:ephemeral_condition : printf(s:permanent_condition, s:config_type, s:node), - \ printf(" return"), - \ printf(" endif"), - \ printf(" call tsdetect#coc#auto#switch_%s_%s()", s:config_type, s:node), - \ printf("endfunction"), - \ ], "\n") - endfor +for [s:node, s:deno] in [["node", "deno"], ["deno", "node"]] execute join([ - \ printf("function! tsdetect#coc#auto#switch_%s() abort", s:config_type), - \ printf(" if !exists('b:tsdetect_is_node')"), + \ printf("function! tsdetect#coc#auto#switch_%s() abort", s:node), + \ printf(" doautocmd User tsdetect#coc#auto#swtich#%s#before", s:node), + \ printf(" let g:tsdetect#coc#auto#switched_%s = 1", s:node), + \ printf(" let g:tsdetect#coc#auto#switched_%s = 0", s:deno), + \ printf(" CocCommand tsdetect.internal.%s.initializeWorkspace", s:node), + \ printf("endfunction"), + \ printf(""), + \ printf("function! tsdetect#coc#auto#switch_%s_if_necessary() abort", s:node), + \ printf(" if get(g:, 'tsdetect#coc#auto#switched_%s', 0)", s:node), \ printf(" return"), \ printf(" endif"), - \ printf(" if b:tsdetect_is_node"), - \ printf(" call tsdetect#coc#auto#switch_%s_node_if_necessary()", s:config_type), - \ printf(" else"), - \ printf(" call tsdetect#coc#auto#switch_%s_deno_if_necessary()", s:config_type), - \ printf(" endif"), + \ printf(" call tsdetect#coc#auto#switch_%s()", s:node), \ printf("endfunction"), \ ], "\n") endfor + +function! tsdetect#coc#auto#switch() abort + if !exists('b:tsdetect_is_node') + return + endif + if b:tsdetect_is_node + call tsdetect#coc#auto#switch_node_if_necessary() + else + call tsdetect#coc#auto#switch_deno_if_necessary() + endif +endfunction diff --git a/package.json b/package.json index 991322a..2469df5 100644 --- a/package.json +++ b/package.json @@ -2,14 +2,7 @@ "name": "coc-tsdetect", "version": "2.1.0", "description": "coc.nvim extensions for detecting node and deno environment.", - "keywords": [ - "deno", - "typescript", - "javascript", - "neovim", - "vim", - "coc.nvim" - ], + "keywords": ["deno", "typescript", "javascript", "neovim", "vim", "coc.nvim"], "license": "MIT", "author": "Luma", "main": "./out/coc.js", @@ -28,12 +21,8 @@ "onLanguage:javascriptreact", "onCommand:tsdetect.manual.deno.initializeWorkspace", "onCommand:tsdetect.manual.node.initializeWorkspace", - "onCommand:tsdetect.internal.ephemeral.deno.initializeWorkspace", - "onCommand:tsdetect.internal.ephemeral.node.initializeWorkspace", - "onCommand:tsdetect.internal.workspace.deno.initializeWorkspace", - "onCommand:tsdetect.internal.workspace.node.initializeWorkspace", - "onCommand:tsdetect.internal.user.deno.initializeWorkspace", - "onCommand:tsdetect.internal.user.node.initializeWorkspace" + "onCommand:tsdetect.internal.deno.initializeWorkspace", + "onCommand:tsdetect.internal.node.initializeWorkspace" ], "contributes": { "commands": [ @@ -48,32 +37,12 @@ "category": "TypeScript Language Server" }, { - "command": "tsdetect.internal.ephemeral.deno.initializeWorkspace", - "title": "Initialize Deno Silently Without Saving Config", - "category": "Deno Language Server" - }, - { - "command": "tsdetect.internal.ephemeral.node.initializeWorkspace", - "title": "Initialize Node Silently Without Saving Config", - "category": "TypeScript Language Server" - }, - { - "command": "tsdetect.internal.user.deno.initializeWorkspace", - "title": "Initialize Deno Silently In User Config", - "category": "Deno Language Server" - }, - { - "command": "tsdetect.internal.user.node.initializeWorkspace", - "title": "Initialize Node Silently In User Config", - "category": "TypeScript Language Server" - }, - { - "command": "tsdetect.internal.workspace.deno.initializeWorkspace", + "command": "tsdetect.internal.deno.initializeWorkspace", "title": "Initialize Deno Silently In Workspace Config", "category": "Deno Language Server" }, { - "command": "tsdetect.internal.workspace.node.initializeWorkspace", + "command": "tsdetect.internal.node.initializeWorkspace", "title": "Initialize Node Silently In Workspace Config", "category": "TypeScript Language Server" } @@ -86,82 +55,47 @@ "type": "string", "default": "auto", "markdownDescription": "How to setup `deno.enabled` and `tsserver.enabled`.\n\n- `\"auto\"` (default): Automatically switching `deno.enabled` and `tsserver.enabled` based on file environment. This is determined whether there is a node_modules directory in one of ancestor directories.\n- `\"auto_user_config\"`: TODO.\n- `\"auto_workspace_config\"`: TODO.\n- `\"manual\"`: coc-tsdetect won't do nothing.\n", - "enum": [ - "auto", - "manual" - ], - "examples": [ - "auto", - "manual" - ] + "enum": ["auto", "manual"], + "examples": ["auto", "manual"] }, - "tsdetect.configType": { - "type": "string", - "default": "ephemeral", - "markdownDescription": "Determine how configuration changed automatically.\n\n", - "enum": [ - "ephemeral", - "workspace", - "user" - ], - "examples": [ - "ephemeral", - "workspace", - "user" - ] + "tsdetect.doNotCreateOnNode": { + "type": "boolean", + "default": true, + "examples": [false, true] }, - "tsdetect.controlTrimSameExts": { + "tsdetect.doNothingIfConfigExists": { "type": "boolean", "default": true, - "markdownDescription": "Whether to control coc.source.file.trimSameExts.", - "examples": [ - true, - false - ] + "examples": [false, true] }, - "tsdetect.controlTrimSameExtsBase": { - "type": "array", - "items": { - "type": "string" + "tsdetect.nodeOverride": { + "type": "object", + "default": { + "deno.lint": false, + "coc.source.file.trimSameExts": [".js", ".ts"], + "prettier.disableLanguage": [] }, - "default": [], - "markdownDescription": "When controlling coc.source.file.trimSameExts, these values are used as base.", "examples": [ - [] + {} ] }, - "tsdetect.controlTrimSameExtsNode": { - "type": "array", - "items": { - "type": "string" - }, - "default": [ - ".ts", - ".js" - ], - "markdownDescription": "Extensions added when using Node environment to `tsdetect.controlTrimSameExtsBase`.", - "examples": [ - [], - [ - ".ts", - ".js" - ], - [ - ".tsx", - ".ts", - ".js" + "tsdetect.denoOverride": { + "type": "object", + "default": { + "deno.lint": true, + "coc.source.file.trimSameExts": [], + "prettier.disableLanguage": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" ] - ] - }, - "tsdetect.controlTrimSameExtsDeno": { - "type": "array", - "items": { - "type": "string" }, - "default": [], - "markdownDescription": "Extensions added when using Deno environment to `tsdetect.controlTrimSameExtsBase`. Maybe you would love to keep it default.", "examples": [ - [] + {}, + { + "deno.unstable": true + } ] } } diff --git a/src/coc_internal.ts b/src/coc_internal.ts new file mode 100644 index 0000000..ec30d06 --- /dev/null +++ b/src/coc_internal.ts @@ -0,0 +1,5 @@ +export enum ConfigurationTarget { + Global, + User, + Workspace, +} diff --git a/src/commands.ts b/src/commands.ts index c1c9222..68cffd3 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -1,55 +1,62 @@ -import { commands, window } from "coc.nvim"; +import { commands, window, workspace } from "coc.nvim"; +import { ConfigurationTarget } from "./coc_internal"; import { setConfigWorkspace } from "./set_config"; -import { getSettings, Settings } from "./settings"; - -const createTrimSameExts = (settings: Settings, target: "node" | "deno") => { - return [ - ...settings.controlTrimSameExtsBase, - ...(target === "node" - ? settings.controlTrimSameExtsNode - : settings.controlTrimSameExtsDeno), - ]; +import { getSettings, Settings, TsRuntime } from "./settings"; + +const configure = async (runtime: TsRuntime, settings: Settings) => { + await setConfigWorkspace("tsserver", "enable", runtime === "node"); + await setConfigWorkspace("deno", "enable", runtime === "deno"); + await commands.executeCommand("editor.action.restart"); + const override = + runtime === "node" ? settings.nodeOverride : settings.denoOverride; + + /* eslint-disable no-restricted-syntax,no-await-in-loop,no-continue */ + for (const key of Object.keys(override)) { + const ns = key.split("."); + const nsKey = ns.pop(); + if (typeof nsKey !== "string") { + await window.showWarningMessage( + `Override key '${key}' does not include any dots.`, + ); + continue; + } + await setConfigWorkspace(ns.join("."), nsKey, override[key]); + } + /* eslint-enable no-restricted-syntax,no-await-in-loop,no-continue */ }; -export const manualInitializeWorkspace = async (target: "node" | "deno") => { - const settings = getSettings(); +export const manualInitializeWorkspace = async (runtime: TsRuntime) => { await setConfigWorkspace("tsdetect", "mode", "manual"); + const settings = getSettings(); - if (target === "node") { - await setConfigWorkspace("tsserver", "enable", true); - await setConfigWorkspace("deno", "enable", false); - } else { - await commands.executeCommand("deno.initializeWorkspace"); - } + await configure(runtime, settings); await commands.executeCommand("editor.action.restart"); await window.showInformationMessage( - `${target === "node" ? "Node" : "Deno"} workspace settings configured!`, + `${runtime === "node" ? "Node" : "Deno"} workspace settings configured!`, ); - - if (settings.controlTrimSameExts) { - await setConfigWorkspace( - "coc.source.file", - "trimSameExts", - createTrimSameExts(settings, target), - ); - } }; -export const configSwitch = async ( - target: "node" | "deno", - setConfig: (ns: string, key: string, value: unknown) => Promise, -) => { +export const autoInitializeWorkspace = async (runtime: TsRuntime) => { + const workspaceConfigFile = workspace.getConfigFile( + ConfigurationTarget.Workspace, + ); const settings = getSettings(); - await setConfig("tsserver", "enable", target === "node"); - await setConfig("deno", "enable", target === "deno"); - if (settings.controlTrimSameExts) { - await setConfig( - "coc.source.file", - "trimSameExts", - createTrimSameExts(settings, target), - ); - } + const exists = typeof workspaceConfigFile === "string"; + + if (settings.doNothingIfConfigExists && exists) return; + if (settings.doNotCreateOnNode && runtime === "node" && !exists) return; + + const tsserverConfig = workspace.getConfiguration("tsserver"); + const denoConfig = workspace.getConfiguration("deno"); + + if (exists && tsserverConfig.get("enable") && runtime === "node") return; + if (exists && denoConfig.get("enable") && runtime === "deno") return; + + await configure(runtime, settings); await commands.executeCommand("editor.action.restart"); + await workspace.nvim.command( + `doautocmd User tsdetect#coc#auto#switch#${runtime}#after`, + ); }; diff --git a/src/entrypoints/coc.ts b/src/entrypoints/coc.ts index e3a0cfb..1a88a76 100644 --- a/src/entrypoints/coc.ts +++ b/src/entrypoints/coc.ts @@ -1,24 +1,19 @@ import { workspace, ExtensionContext, commands } from "coc.nvim"; import assert from "assert"; -import { getSetConfig } from "../set_config"; -import { configSwitch, manualInitializeWorkspace } from "../commands"; -import { ConfigType, EXTENSION_NS, getSettings } from "../settings"; +import { + autoInitializeWorkspace, + manualInitializeWorkspace, +} from "../commands"; +import { EXTENSION_NS, getSettings } from "../settings"; const initialize = async (_context: ExtensionContext) => { const settings = getSettings(); const proms: Promise[] = []; - proms.push( - workspace.nvim.call("tsdetect#coc#setup_switch", [ - settings.mode, - settings.configType, - ]), - ); + proms.push(workspace.nvim.call("tsdetect#coc#setup_switch", [settings.mode])); if (settings.mode === "auto") { - proms.push( - workspace.nvim.call(`tsdetect#coc#auto#switch_${settings.configType}`), - ); + proms.push(workspace.nvim.call(`tsdetect#coc#auto#switch`)); } await Promise.all(proms); @@ -57,15 +52,11 @@ export const activate = async (context: ExtensionContext) => { // Setup commands for automatic configuration. (["deno", "node"] as const).forEach((target) => { - (["ephemeral", "workspace", "user"] as const).forEach( - (configType: ConfigType) => { - context.subscriptions.push( - commands.registerCommand( - `${EXTENSION_NS}.internal.${configType}.${target}.initializeWorkspace`, - () => configSwitch(target, getSetConfig(configType)), - ), - ); - }, + context.subscriptions.push( + commands.registerCommand( + `${EXTENSION_NS}.internal.${target}.initializeWorkspace`, + () => autoInitializeWorkspace(target), + ), ); }); diff --git a/src/globals.d.ts b/src/globals.d.ts new file mode 100644 index 0000000..bdf5813 --- /dev/null +++ b/src/globals.d.ts @@ -0,0 +1,9 @@ +declare module "coc.nvim" { + import type { ConfigurationTarget } from "./coc_internal"; + + export namespace workspace { + export function getConfigFile( + target: ConfigurationTarget, + ): string | undefined; + } +} diff --git a/src/set_config.ts b/src/set_config.ts index b248c60..a08dc65 100644 --- a/src/set_config.ts +++ b/src/set_config.ts @@ -1,22 +1,4 @@ import { workspace } from "coc.nvim"; -import { ConfigType } from "./settings"; - -export const setConfigEphemeral = async ( - ns: string, - key: string, - value: unknown, -) => { - await workspace.nvim.call("coc#config", [`${ns}.${key}`, value]); -}; - -export const setConfigUser = async ( - ns: string, - key: string, - value: unknown, -) => { - const config = workspace.getConfiguration(ns); - config.update(key, value, true); -}; export const setConfigWorkspace = async ( ns: string, @@ -26,16 +8,3 @@ export const setConfigWorkspace = async ( const config = workspace.getConfiguration(ns); config.update(key, value); }; - -export const getSetConfig = (configType: ConfigType) => { - switch (configType) { - case "ephemeral": - return setConfigEphemeral; - case "workspace": - return setConfigWorkspace; - case "user": - return setConfigUser; - default: - throw new Error("Illegal configType."); - } -}; diff --git a/src/settings.ts b/src/settings.ts index 0465eee..abf67da 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -3,23 +3,24 @@ import assert from "assert"; export const EXTENSION_NS = "tsdetect"; -export type ConfigType = "ephemeral" | "user" | "workspace"; +export type TsRuntime = "node" | "deno"; export interface Settings { + /** @default "auto" */ mode: "auto" | "manual"; - configType: ConfigType; - controlTrimSameExts: boolean; - controlTrimSameExtsBase: string[]; - controlTrimSameExtsNode: string[]; - controlTrimSameExtsDeno: string[]; + /** @default true */ + doNotCreateOnNode: boolean; + /** @default true */ + doNothingIfConfigExists: boolean; + denoOverride: { [key: string]: unknown }; + nodeOverride: { [key: string]: unknown }; } export const settingsKeys = [ "mode", - "configType", - "controlTrimSameExts", - "controlTrimSameExtsBase", - "controlTrimSameExtsNode", - "controlTrimSameExtsDeno", + "doNotCreateOnNode", + "doNothingIfConfigExists", + "denoOverride", + "nodeOverride", ]; export const getSettings = (): Settings => {