diff --git a/.github/workflows/activate.yml b/.github/workflows/activate.yml new file mode 100644 index 0000000..8b2c4b2 --- /dev/null +++ b/.github/workflows/activate.yml @@ -0,0 +1,40 @@ +name: Activate + +on: + push: + branches: + - master + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + emacs-version: + - 28.1 + + steps: + - uses: actions/checkout@v2 + + - uses: jcs090218/setup-emacs@master + with: + version: ${{ matrix.emacs-version }} + + - uses: actions/setup-node@v2 + with: + node-version: '16' + + - uses: emacs-eask/setup-eask@master + with: + version: 'snapshot' + + - name: Run tests + run: + make activate diff --git a/Eask b/Eask index 541d73d..a05134b 100644 --- a/Eask +++ b/Eask @@ -1,6 +1,6 @@ (package "lsp-grammarly" - "0.2.2" - "LSP Clients for Grammarly ") + "0.3.0" + "LSP Clients for Grammarly") (package-file "lsp-grammarly.el") @@ -14,4 +14,7 @@ (depends-on "s") (depends-on "ht") +(development + (depends-on "markdown-mode")) + (setq network-security-level 'low) ; see https://github.com/jcs090218/setup-emacs-windows/issues/156#issuecomment-932956432 diff --git a/Makefile b/Makefile index c9b20af..5e6bc22 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,9 @@ SHELL := /usr/bin/env bash EMACS ?= emacs EASK ?= eask -TEST-FILES := $(shell ls test/lsp-grammarly-*.el) - .PHONY: clean checkdoc lint package install compile test -ci: clean package install compile +ci: clean package install compile checkdoc lint package: @echo "Packaging..." @@ -23,7 +21,19 @@ compile: test: @echo "Testing..." - $(EASK) exec ert-runner -L . $(LOAD-TEST-FILES) -t '!no-win' -t '!org' + $(EASK) ert ./test/*.el + +checkdoc: + @echo "Run checkdoc..." + $(EASK) checkdoc + +lint: + @echo "Run package-lint..." + $(EASK) lint clean: rm -rf .eask *.elc + +activate: + $(EASK) install --dev + $(EASK) load ./test/activate.el diff --git a/README.md b/README.md index 94f215a..08c7dfc 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,9 @@ # lsp-grammarly [![CI](https://github.com/emacs-grammarly/lsp-grammarly/actions/workflows/test.yml/badge.svg)](https://github.com/emacs-grammarly/lsp-grammarly/actions/workflows/test.yml) +[![Activate](https://github.com/emacs-grammarly/lsp-grammarly/actions/workflows/activate.yml/badge.svg)](https://github.com/emacs-grammarly/lsp-grammarly/actions/workflows/activate.yml) -`lsp-mode` client leveraging [unofficial-grammarly-language-server](https://github.com/emacs-grammarly/unofficial-grammarly-language-server). +`lsp-mode` client leveraging [grammarly-language-server](https://github.com/emacs-grammarly/grammarly-language-server).

@@ -18,10 +19,6 @@ - [📇 Commands](#📇-commands) - [🔧 Configuration](#🔧-configuration) - [📝 Roadmap](#📝-roadmap) - - [💸 Using a Paid Grammarly Account](#💸-using-a-paid-grammarly-account) - - [🔍 Method 1: Login with VSCode (easier)](#🔍-method-1-login-with-vscode-easier) - - [🔍 Method 2: Login with Emacs (a bit complicated)](#🔍-method-2-login-with-emacs-a-bit-complicated) - - [Authentication from Grammarly website](#authentication-from-grammarly-website) - [Contribute](#contribute) @@ -38,6 +35,9 @@ ## 📇 Commands +> ⚠️ We are moving to the newer grammarly-language-server that uses the official +> API. Some features are disabled for now, but we will later add them back. + List of commands interact with `language server` and `Grammarly.com`. | Commands | Description | @@ -51,16 +51,42 @@ List of commands interact with `language server` and `Grammarly.com`. ## 🔧 Configuration `lsp-grammarly` supports following configuration. Each configuration is described in -detail in [Grammarly Settings](https://github.com/emacs-grammarly/unofficial-grammarly-language-server#extension-settings). - -* `grammarly.autoActivate` via `lsp-grammarly-auto-activate` -* `grammarly.audience` via `lsp-grammarly-audience` -* `grammarly.dialect` via `lsp-grammarly-dialect` -* `grammarly.domain` via `lsp-grammarly-domain` -* `grammarly.emotions` via `lsp-grammarly-emotions` -* `grammarly.goals` via `lsp-grammarly-goals` -* `grammarly.userWords` via `lsp-grammarly-user-words` -* `grammarly.overrides` via `lsp-grammarly-override` +detail in [Grammarly Extension Settings](https://github.com/emacs-grammarly/grammarly-language-server/blob/main/extension/package.json). + +* `grammarly.patterns` via `lsp-grammarly-patterns` +* `grammarly.selectors` via `lsp-grammarly-selectors` +* `grammarly.config.documentDialect` via `lsp-grammarly-dialect` +* `grammarly.config.documentDomain` via `lsp-grammarly-domain` +* `grammarly.config.suggestions.ConjunctionAtStartOfSentence` via `lsp-grammarly-suggestions-conjunction-at-start-of-sentence` +* `grammarly.config.suggestions.Fluency` via `lsp-grammarly-suggestions-fluency` +* `grammarly.config.suggestions.InformalPronounsAcademic` via `lsp-grammarly-suggestions-informal-pronouns-academic` +* `grammarly.config.suggestions.MissingSpaces` via `lsp-grammarly-suggestions-missing-spaces` +* `grammarly.config.suggestions.NounStrings` via `lsp-grammarly-suggestions-noun-strings` +* `grammarly.config.suggestions.NumbersBeginningSentences` via `lsp-grammarly-suggestions-numbers-beginning-sentences` +* `grammarly.config.suggestions.NumbersZeroThroughTen` via `lsp-grammarly-suggestions-numbers-zero-through-ten` +* `grammarly.config.suggestions.OxfordComma` via `lsp-grammarly-suggestions-oxford-comma` +* `grammarly.config.suggestions.PassiveVoice` via `lsp-grammarly-suggestions-passive-voice` +* `grammarly.config.suggestions.PersonFirstLanguage` via `lsp-grammarly-suggestions-person-first-language` +* `grammarly.config.suggestions.PossiblyBiasedLanguageAgeRelated` via `lsp-grammarly-suggestions-possibly-biased-language-age-related` +* `grammarly.config.suggestions.PossiblyBiasedLanguageDisabilityRelated` via `lsp-grammarly-suggestions-possibly-biased-language-disability-related` +* `grammarly.config.suggestions.PossiblyBiasedLanguageFamilyRelated` via `lsp-grammarly-suggestions-possibly-biased-language-family-related` +* `grammarly.config.suggestions.PossiblyBiasedLanguageGenderRelated` via `lsp-grammarly-suggestions-possibly-biased-language-gender-related` +* `grammarly.config.suggestions.PossiblyBiasedLanguageHumanRights` via `lsp-grammarly-suggestions-possibly-biased-language-human-rights` +* `grammarly.config.suggestions.PossiblyBiasedLanguageHumanRightsRelated` via `lsp-grammarly-suggestions-possibly-biased-language-human-rights-related` +* `grammarly.config.suggestions.PossiblyBiasedLanguageLgbtqiaRelated` via `lsp-grammarly-suggestions-possibly-biased-language-lgbtqia-related` +* `grammarly.config.suggestions.PossiblyBiasedLanguageRaceEthnicityRelated` via `lsp-grammarly-suggestions-possibly-biased-language-race-ethnicity-related` +* `grammarly.config.suggestions.PossiblyPoliticallyIncorrectLanguage` via `lsp-grammarly-suggestions-possibly-politically-incorrect-language` +* `grammarly.config.suggestions.PrepositionAtTheEndOfSentence` via `lsp-grammarly-suggestions-preposition-at-the-end-of-sentence` +* `grammarly.config.suggestions.PunctuationWithQuotation` via `lsp-grammarly-suggestions-punctuation-with-quotation` +* `grammarly.config.suggestions.ReadabilityFillerwords` via `lsp-grammarly-suggestions-readability-fillerwords` +* `grammarly.config.suggestions.ReadabilityTransforms` via `lsp-grammarly-suggestions-readability-transforms` +* `grammarly.config.suggestions.SentenceVariety` via `lsp-grammarly-suggestions-sentence-variety` +* `grammarly.config.suggestions.SpacesSurroundingSlash` via `lsp-grammarly-suggestions-spaces-surrounding-slash` +* `grammarly.config.suggestions.SplitInfinitive` via `lsp-grammarly-suggestions-split-infinitive` +* `grammarly.config.suggestions.StylisticFragments` via `lsp-grammarly-suggestions-stylistic-fragments` +* `grammarly.config.suggestions.UnnecessaryEllipses` via `lsp-grammarly-suggestions-unnecessary-ellipses` +* `grammarly.config.suggestions.Variety` via `lsp-grammarly-suggestions-variety` +* `grammarly.config.suggestions.Vocabulary` via `lsp-grammarly-suggestions-vocabulary` ## 📝 Roadmap @@ -68,66 +94,6 @@ List of todos, but I have not got time to implement these features. - [ ] Create another package that displays information from [Grammarly.com](https://www.grammarly.com/) (To display useful information, `score`, `readability`, `word counts`, etc). -- [ ] Implement command `Ignore Grammarly Issue`. -- [ ] Implement `diagnostics` and `severity` for configuration. - -## 💸 Using a Paid Grammarly Account - -> ***NOTE:** To login, make sure you have package [keytar](https://github.com/emacs-grammarly/keytar) -set up properly. See [keytar#installation](https://github.com/emacs-grammarly/keytar#installation) -for setup instruction.* - -You can either login with [vscode-grammarly](https://marketplace.visualstudio.com/items?itemName=znck.grammarly) -using VSCode or hit `M-x lsp-grammarly-login`. They both share the same credentials -so you can login with either side. - -### 🔍 Method 1: Login with VSCode (easier) - -Install VSCode and install extension [vscode-grammarly](https://marketplace.visualstudio.com/items?itemName=znck.grammarly) -from the extension panel. - -

- -Then call command palette (default to Ctrl+Shift+p) -and type to search `grammarly login` command. - -

- -You should see [Grammarly Website](#authentication-from-grammarly-website) and -login with your Grammarly account. - -

- -🎉 Make sure you click on the button `Open Visual Studio Code`. Done! You -can now close VSCode and go back to Emacs! - -### 🔍 Method 2: Login with Emacs (a bit complicated) - -Hit `M-x lsp-grammarly-login` and you should see the Grammarly's website pop out -from your favorite browser. See below [screenshot](#authentication-from-grammarly-website), - -After login, click the button `Open URL:vscode`, If you have VSCode installed, then -this button would be `Open Visual Studio Code` instead yet it doesn't matter. - -

- -Then click F12 to open the DevTool window. You should able to see -an URI like the following - -

- -Copy and paste the URI back to Emacs and hit return. - -

- -🎉 Done! Now you should be loggin! - -### Authentication from Grammarly website - -Login with your Grammarly account (This step does not require VSCode to be -installed)! - -

## Contribute diff --git a/lsp-grammarly.el b/lsp-grammarly.el index a38e717..ab5ab17 100644 --- a/lsp-grammarly.el +++ b/lsp-grammarly.el @@ -6,7 +6,7 @@ ;; Author: Shen, Jen-Chieh ;; Description: LSP Clients for Grammarly. ;; Keyword: lsp grammarly checker -;; Version: 0.2.2 +;; Version: 0.3.0 ;; Package-Requires: ((emacs "27.1") (lsp-mode "6.1") (grammarly "0.3.0") (request "0.3.0") (s "1.12.0") (ht "2.3")) ;; URL: https://github.com/emacs-grammarly/lsp-grammarly @@ -41,9 +41,6 @@ (require 'ht) (require 'json) -(unless (require 'keytar nil t) - (warn "`keytar' is required for login into Grammarly account")) - (defgroup lsp-grammarly nil "Settings for the Grammarly Language Server. @@ -67,9 +64,17 @@ This is only for development use." :type 'list :group 'lsp-grammarly) -(defcustom lsp-grammarly-auto-activate t - "Enable Grammarly service when a supported document is opened." - :type 'boolean +(defcustom lsp-grammarly-patterns + ["**/**.md" + "**/*.txt"] + "A glob pattern, like `*.{md,txt}` for file scheme." + :type 'vector + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-selectors + [] + "Filter documents to be checked with Grammarly." + :type 'vector :group 'lsp-grammarly) (defcustom lsp-grammarly-audience "knowledgeable" @@ -84,7 +89,8 @@ This is only for development use." :type '(choice (const "american") (const "australian") (const "british") - (const "canadian")) + (const "canadian") + (const "auto-text")) :group 'lsp-grammarly) (defcustom lsp-grammarly-domain "general" @@ -92,42 +98,237 @@ This is only for development use." :type '(choice (const "academic") (const "business") (const "general") - (const "technical") + (const "mail") (const "casual") (const "creative")) :group 'lsp-grammarly) +(defcustom lsp-grammarly-suggestions-conjunction-at-start-of-sentence + nil + "Flags use of conjunctions such as 'but' and 'and' at the beginning of +sentences." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-fluency + t + "Suggests ways to sound more natural and fluent." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-informal-pronouns-academic + nil + "Flags use of personal pronouns such as 'I' and 'you' in academic writing." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-missing-spaces + t + "Suggests adding missing spacing after a numeral when writing times." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-noun-strings + t + "Flags a series of nouns that modify a final noun." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-numbers-beginning-sentences + t + "Suggests spelling out numbers at the beginning of sentences." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-numbers-zero-through-ten + t + "Suggests spelling out numbers zero through ten." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-oxford-comma + nil + "Suggests adding the Oxford comma after the second-to-last item in a list of +things." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-passive-voice + nil + "Flags use of passive voice." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-person-first-language + t + "Suggests using person-first language to refer respectfully to an individual +with a disability." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-possibly-biased-language-age-related + t + "Suggests alternatives to potentially biased language related to older adults." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-possibly-biased-language-disability-related + t + "Suggests alternatives to potentially ableist language." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-possibly-biased-language-family-related + t + "Suggests alternatives to potentially biased language related to parenting and +family systems." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-possibly-biased-language-gender-related + t + "Suggests alternatives to potentially gender-biased and non-inclusive phrasing." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-possibly-biased-language-human-rights + t + "Suggests alternatives to language related to human slavery." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-possibly-biased-language-human-rights-related + t + "Suggests alternatives to terms with origins in the institution of slavery." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-possibly-biased-language-lgbtqia-related + t + "Flags LGBTQIA+-related terms that may be seen as biased, outdated, or +disrespectful in some contexts." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-possibly-biased-language-race-ethnicity-related + t + "Suggests alternatives to potentially biased language related to race and +ethnicity." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-possibly-politically-incorrect-language + t + "Suggests alternatives to language that may be considered politically +incorrect." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-preposition-at-the-end-of-sentence + nil + "Flags use of prepositions such as 'with' and 'in' at the end of sentences." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-punctuation-with-quotation + t + "Suggests placing punctuation before closing quotation marks." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-readability-fillerwords + t + "Flags long, complicated sentences that could potentially confuse your reader." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-readability-transforms + t + "Suggests splitting long, complicated sentences that could potentially confuse +your reader." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-sentence-variety + t + "Flags series of sentences that follow the same pattern." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-spaces-surrounding-slash + t + "Suggests removing extra spaces surrounding a slash." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-split-infinitive + t + "Suggests rewriting split infinitives so that an adverb doesn't come between +'to' and the verb." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-stylistic-fragments + nil + "Suggests completing all incomplete sentences, including stylistic sentence +fragments that may be intentional." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-unnecessary-ellipses + nil + "Flags unnecessary use of ellipses (...)." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-variety + t + "Suggests alternatives to words that occur frequently in the same paragraph." + :type 'boolean + :group 'lsp-grammarly) + +(defcustom lsp-grammarly-suggestions-vocabulary + t + "Suggests alternatives to bland and overused words such as 'good' and 'nice'." + :type 'boolean + :group 'lsp-grammarly) + +(defvar lsp-grammarly--show-debug-message nil + "Flag to see if we show debug messages.") + +;; +;; (@* "Obsolete" ) +;; + +(defcustom lsp-grammarly-auto-activate t + "Enable Grammarly service when a supported document is opened." + :type 'boolean + :group 'lsp-grammarly) +(make-obsolete-variable 'lsp-grammarly-auto-activate nil "0.2.2") + (defcustom lsp-grammarly-emotions '() "Experimental: How do you want to sound." :type 'list :group 'lsp-grammarly) +(make-obsolete-variable 'lsp-grammarly-emotions nil "0.3.0") (defcustom lsp-grammarly-goals '() "Experimental: What are you trying to do." :type 'list :group 'lsp-grammarly) +(make-obsolete-variable 'lsp-grammarly-goal nil "0.3.0") (defcustom lsp-grammarly-user-words '() "A list of words as a local dictionary." :type 'list :group 'lsp-grammarly) +(make-obsolete-variable 'lsp-grammarly-user-words nil "0.3.0") (defcustom lsp-grammarly-override '() "Per document override for audience, dialect, domain, emotions and goals." :type 'list :group 'lsp-grammarly) - -(defvar lsp-grammarly--show-debug-message nil - "Flag to see if we show debug messages.") - -;; -;; (@* "External" ) -;; - -(declare-function keytar--check "ext:keytar.el") -(declare-function keytar-set-password "ext:keytar.el") -(declare-function keytar-get-password "ext:keytar.el") -(declare-function keytar-delete-password "ext:keytar.el") +(make-obsolete-variable 'lsp-grammarly-override nil "0.3.0") ;; ;; (@* "Util" ) @@ -149,10 +350,6 @@ This is only for development use." (dotimes (_ n) (insert (elt charset (random baseCount)))) (buffer-string)))) -(defun lsp-grammarly--json-encode (obj) - "Wrap function `json-encode' to convert OBJ for keytar password." - (s-replace "\"" "\\\"" (json-encode obj))) - (defun lsp-grammarly--json-read (string) "Ensure read JSON STRING avoid bad string format." (let ((output (or (ignore-errors (json-read-from-string string)) @@ -166,15 +363,6 @@ This is only for development use." ;; (@* "Login" ) ;; -(defconst lsp-grammarly--cookie-key "vscode-grammarly-cookie" - "Key to store credentials.") - -(defconst lsp-grammarly--account "default" - "Key that Grammarly LSP default to.") - -(defvar lsp-grammarly--password-string nil - "Encrypted password in string.") - (defvar lsp-grammarly--password nil "Encrypted password in alist.") @@ -202,20 +390,12 @@ For argument CALLBACK, see object `lsp--client' description." (defun lsp-grammarly--store-token (_workspace _uri _callback &rest _) "Save the token once." - (keytar-set-password - lsp-grammarly--cookie-key lsp-grammarly--account lsp-grammarly--password-string)) + ) (defun lsp-grammarly--init (&rest _) "Get Grammarly API ready." - (unless (lsp-grammarly-login-p) - (let ((pass (ignore-errors - (keytar-get-password lsp-grammarly--cookie-key lsp-grammarly--account)))) - (when pass - (setq lsp-grammarly--password-string pass - lsp-grammarly--password (lsp-grammarly--json-read pass)))) - (if (lsp-grammarly-login-p) - (message "[INFO] Logged in as, %s" (lsp-grammarly--username)) - (message "[INFO] Visited as, anonymous")))) + ;; TODO: wait for the server side implementation + ) (defun lsp-grammarly--show-error (_workspace _uri callback &rest _) "Show error from language server. @@ -244,19 +424,45 @@ For argument CALLBACK, see object `lsp--client' description." (list (lsp-package-path 'grammarly-ls) "--stdio"))) (lsp-register-custom-settings - '(("grammarly.autoActivate" lsp-grammarly-auto-activate t) - ("grammarly.audience" lsp-grammarly-audience) - ("grammarly.dialect" lsp-grammarly-dialect) - ("grammarly.domain" lsp-grammarly-domain) - ("grammarly.emotions" lsp-grammarly-emotions) - ("grammarly.goals" lsp-grammarly-goals) - ("grammarly.userWords" lsp-grammarly-user-words) - ("grammarly.overrides" lsp-grammarly-override))) + '(("grammarly.patterns" lsp-grammarly-patterns) + ("grammarly.selectors" lsp-grammarly-selectors) + ("grammarly.config.documentDialect" lsp-grammarly-dialect) + ("grammarly.config.documentDomain" lsp-grammarly-domain) + ("grammarly.config.suggestions.ConjunctionAtStartOfSentence" lsp-grammarly-suggestions-conjunction-at-start-of-sentence) + ("grammarly.config.suggestions.Fluency" lsp-grammarly-suggestions-fluency) + ("grammarly.config.suggestions.InformalPronounsAcademic" lsp-grammarly-suggestions-informal-pronouns-academic) + ("grammarly.config.suggestions.MissingSpaces" lsp-grammarly-suggestions-missing-spaces) + ("grammarly.config.suggestions.NounStrings" lsp-grammarly-suggestions-noun-strings) + ("grammarly.config.suggestions.NumbersBeginningSentences" lsp-grammarly-suggestions-numbers-beginning-sentences) + ("grammarly.config.suggestions.NumbersZeroThroughTen" lsp-grammarly-suggestions-numbers-zero-through-ten) + ("grammarly.config.suggestions.OxfordComma" lsp-grammarly-suggestions-oxford-comma) + ("grammarly.config.suggestions.PassiveVoice" lsp-grammarly-suggestions-passive-voice) + ("grammarly.config.suggestions.PersonFirstLanguage" lsp-grammarly-suggestions-person-first-language) + ("grammarly.config.suggestions.PossiblyBiasedLanguageAgeRelated" lsp-grammarly-suggestions-possibly-biased-language-age-related) + ("grammarly.config.suggestions.PossiblyBiasedLanguageDisabilityRelated" lsp-grammarly-suggestions-possibly-biased-language-disability-related) + ("grammarly.config.suggestions.PossiblyBiasedLanguageFamilyRelated" lsp-grammarly-suggestions-possibly-biased-language-family-related) + ("grammarly.config.suggestions.PossiblyBiasedLanguageGenderRelated" lsp-grammarly-suggestions-possibly-biased-language-gender-related) + ("grammarly.config.suggestions.PossiblyBiasedLanguageHumanRights" lsp-grammarly-suggestions-possibly-biased-language-human-rights) + ("grammarly.config.suggestions.PossiblyBiasedLanguageHumanRightsRelated" lsp-grammarly-suggestions-possibly-biased-language-human-rights-related) + ("grammarly.config.suggestions.PossiblyBiasedLanguageLgbtqiaRelated" lsp-grammarly-suggestions-possibly-biased-language-lgbtqia-related) + ("grammarly.config.suggestions.PossiblyBiasedLanguageRaceEthnicityRelated" lsp-grammarly-suggestions-possibly-biased-language-race-ethnicity-related) + ("grammarly.config.suggestions.PossiblyPoliticallyIncorrectLanguage" lsp-grammarly-suggestions-possibly-politically-incorrect-language) + ("grammarly.config.suggestions.PrepositionAtTheEndOfSentence" lsp-grammarly-suggestions-preposition-at-the-end-of-sentence) + ("grammarly.config.suggestions.PunctuationWithQuotation" lsp-grammarly-suggestions-punctuation-with-quotation) + ("grammarly.config.suggestions.ReadabilityFillerwords" lsp-grammarly-suggestions-readability-fillerwords) + ("grammarly.config.suggestions.ReadabilityTransforms" lsp-grammarly-suggestions-readability-transforms) + ("grammarly.config.suggestions.SentenceVariety" lsp-grammarly-suggestions-sentence-variety) + ("grammarly.config.suggestions.SpacesSurroundingSlash" lsp-grammarly-suggestions-spaces-surrounding-slash) + ("grammarly.config.suggestions.SplitInfinitive" lsp-grammarly-suggestions-split-infinitive) + ("grammarly.config.suggestions.StylisticFragments" lsp-grammarly-suggestions-stylistic-fragments) + ("grammarly.config.suggestions.UnnecessaryEllipses" lsp-grammarly-suggestions-unnecessary-ellipses) + ("grammarly.config.suggestions.Variety" lsp-grammarly-suggestions-variety) + ("grammarly.config.suggestions.Vocabulary" lsp-grammarly-suggestions-vocabulary))) (lsp-dependency 'grammarly-ls '(:system "grammarly-ls") - '(:npm :package "@emacs-grammarly/unofficial-grammarly-language-server" - :path "unofficial-grammarly-language-server")) + '(:npm :package "@emacs-grammarly/grammarly-languageserver" + :path "grammarly-languageserver")) (lsp-register-client (make-lsp-client @@ -282,178 +488,31 @@ For argument CALLBACK, see object `lsp--client' description." (defun lsp-grammarly-check-grammar () "Start the Grammarly checker." (interactive) - (lsp-request-async - "$/checkGrammar" `(:uri ,(lsp--buffer-uri)) - (lambda (_) (message "Start Grammarly checker...")))) + (user-error "[INFO] This command is currently disabled, and it will be added back in the later version")) (defun lsp-grammarly-stop () "Stop the Grammarly checker." (interactive) - (lsp-request-async - "$/stop" `(:uri ,(lsp--buffer-uri)) - (lambda (_) (message "Stop Grammarly checker...")))) + (user-error "[INFO] This command is currently disabled, and it will be added back in the later version")) (defun lsp-grammarly-stats () "Return document state." (interactive) - (lsp-request-async - "$/getDocumentState" `(:uri ,(lsp--buffer-uri)) - (lambda (state) - (message - (concat - (let* ((user (ht-get state "user")) - (is-premium (ht-get user "isPremium")) - (_is-anonymous (ht-get user "isAnonymous")) - (username (ht-get user "username"))) - (format "[User] %s (%s)" username (if is-premium "Premium" "Free"))) - (when-let ((score (ht-get state "score"))) - (format "\n[Text Score] %s out of 100" score)) - (when-let* ((text-info (ht-get state "textInfo")) - (chars-count (ht-get text-info "charsCount")) - (words-count (ht-get text-info "wordsCount")) - (readability-score (ht-get text-info "readabilityScore"))) - (format "\n[Text-Info] Readability: %s, C: %s, W: %s" - readability-score chars-count words-count)) - (when-let* ((scores (ht-get state "scores")) - (clarity (ht-get scores "Clarity")) - (tone (ht-get scores "Tone")) - (correctness (ht-get scores "Correctness")) - (general-score (ht-get scores "GeneralScore")) - (engagement (ht-get scores "Engagement"))) - (format "\nClarity: %s, Tone: %s, Correctness: %s, GeneralScore: %s, Engagement: %s" - (lsp-grammarly--scale-100 clarity) - (lsp-grammarly--scale-100 tone) - (lsp-grammarly--scale-100 correctness) - (lsp-grammarly--scale-100 general-score) - (lsp-grammarly--scale-100 engagement)))))))) + (user-error "[INFO] This command is currently disabled, and it will be added back in the later version")) ;; ;; (@* "Login" ) ;; -(defvar lsp-grammarly--code-verifier nil "Login information, code verifier.") -(defvar lsp-grammarly--challenge nil "Login information, challenge.") - -(defconst lsp-grammarly-client-id "extensionVSCode" - "Key for URI scheme.") - -(defun lsp-grammarly--resolve-uri (uri) - "Handle URI for authentication." - (let ((prefix "vscode://znck.grammarly/auth/callback?") query) - (if (not (string-prefix-p prefix uri)) - (user-error "[WARNING] An URL should start with prefix: %s" prefix) - (setq uri (s-replace prefix "" uri) - query (url-parse-query-string uri)) - (nth 1 (assoc "code" query))))) - -(defun lsp-grammarly--update-cookie () - "Refresh the Grammarly.com cookie once." - (grammarly--form-cookie) - `((csrf-token . ,(grammarly--get-cookie-by-name "csrf-token")) - (grauth . ,(grammarly--get-cookie-by-name "grauth")) - (gnar-containerId . ,(grammarly--get-cookie-by-name "gnar_containerId")) - (tdi . ,(grammarly--get-cookie-by-name "tdi")))) - -(defun lsp-grammarly--uri-callback () - "Callback after resolving URI. - -Argument CODE is the query string from URI." - (let* ((uri (read-string "[Grammarly Authentication] code: ")) - (code (lsp-grammarly--resolve-uri uri)) - cookie csrf-token grauth gnar-containerId tdi) - (request - (format "https://auth.grammarly.com/v3/user/oranonymous?app=%s" lsp-grammarly-client-id) - :type "GET" - :headers - `(("x-client-type". ,lsp-grammarly-client-id) - ("x-client-version" . "0.0.0")) - :success - (cl-function - (lambda (&key _response _data &allow-other-keys) - (setq cookie (lsp-grammarly--update-cookie) - csrf-token (cdr (assoc 'csrf-token cookie)) - grauth (cdr (assoc 'grauth cookie)) - gnar-containerId (cdr (assoc 'gnar-containerId cookie))) - (request - "https://auth.grammarly.com/v3/api/unified-login/code/exchange" - :type "POST" - :headers - `(("Accept" . "application/json") - ("Content-Type" . "application/json") - ("x-client-type" . ,lsp-grammarly-client-id) - ("x-client-version" . "0.0.0") - ("x-csrf-token" . ,csrf-token) - ("x-container-id" . ,gnar-containerId) - ("cookie" . ,(format "grauth=%s; csrf-token=%s" grauth csrf-token))) - :data - (json-encode - `(("client_id" . ,lsp-grammarly-client-id) - ("code" . ,code) - ("code_verifier" . ,lsp-grammarly--code-verifier))) - :success - (cl-function - (lambda (&key _response data &allow-other-keys) - (setq cookie (lsp-grammarly--update-cookie) - csrf-token (cdr (assoc 'csrf-token cookie)) - grauth (cdr (assoc 'grauth cookie)) - gnar-containerId (cdr (assoc 'gnar-containerId cookie)) - tdi (cdr (assoc 'tdi cookie))) - (let* ((all-data (lsp-grammarly--json-read data)) - (user (nth 0 all-data)) - (premium (string= "Premium" (cdr (assoc 'type user)))) - (name (cdr (assoc 'name user))) - (email (cdr (assoc 'email user))) - (token (format "grauth=%s;csrf-token=%s;tdi=%s;" grauth csrf-token tdi)) - (auth-info `(("isAnonymous" . :json-false) - ("isPremium" . ,premium) - ("token" . ,token) - ("username" . ,email)))) - (keytar-set-password - lsp-grammarly--cookie-key lsp-grammarly--account - (lsp-grammarly--json-encode auth-info)) - ;; TODO: This is slow, need to improve the performance for better - ;; user experience. - (ignore-errors (lsp-workspace-restart nil)) - (message "[INFO] Logged in as `%s`" name)))) - :error - (cl-function - (lambda (&rest args &key _error-thrown &allow-other-keys) - (lsp-grammarly--message "[ERROR] Error while authenticating login: %s" args)))))) - :error - (cl-function - (lambda (&rest args &key _error-thrown &allow-other-keys) - (lsp-grammarly--message "[ERROR] Error while getting cookie: %s" args)))))) - (defun lsp-grammarly-login () "Login to Grammarly.com." (interactive) - (keytar--check) - (if (lsp-grammarly-login-p) - (message "[INFO] You are already logged in with `%s`" (lsp-grammarly--username)) - (setq lsp-grammarly--code-verifier - (base64url-encode-string (lsp-grammarly--random-bytes 96) t) - lsp-grammarly--challenge - (base64url-encode-string (secure-hash 'sha256 lsp-grammarly--code-verifier nil nil t) t)) - (browse-url (format - "https://grammarly.com/signin/app?client_id=%s&code_challenge=%s" - lsp-grammarly-client-id lsp-grammarly--challenge)) - (lsp-grammarly--uri-callback))) + (user-error "[INFO] This command is currently disabled, and it will be added back in the later version")) (defun lsp-grammarly-logout () "Logout from Grammarly.com." (interactive) - (keytar--check) - (if (not (lsp-grammarly-login-p)) - (message "[INFO] You are already logout from Grammarly.com") - (if (keytar-delete-password lsp-grammarly--cookie-key lsp-grammarly--account) - (progn - (setq lsp-grammarly--password nil - lsp-grammarly--password-string nil) - ;; TODO: This is slow, need to improve the performance for better - ;; user experience. - (ignore-errors (lsp-workspace-restart nil)) - (message "[INFO] Logged out of Grammarly.com")) - (message "[ERROR] Failed to logout from Grammarly.com")))) + (user-error "[INFO] This command is currently disabled, and it will be added back in the later version")) (provide 'lsp-grammarly) ;;; lsp-grammarly.el ends here diff --git a/test/activate.el b/test/activate.el new file mode 100644 index 0000000..2efee57 --- /dev/null +++ b/test/activate.el @@ -0,0 +1,65 @@ +;;; activate.el --- Test activation -*- lexical-binding: t; -*- + +;; Copyright (C) 2022 Shen, Jen-Chieh +;; Created date 2022-05-08 12:50:37 + +;; This file is NOT part of GNU Emacs. + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: +;; +;; Test activation +;; + +;;; Code: + +(eask-pkg-init) + +(require 'lsp-mode) + +(lsp-install-server t 'grammarly-ls) ; Start installation + +(defconst timeout 180 + "Timeout in seconds.") + +(defvar timer 0) + +(defun get-lsp-install-buffer () + "Get lsp-insall buffer." + (nth 0 + (cl-remove-if-not (lambda (buf) + (string-prefix-p "*lsp-install:" (buffer-name buf))) + (buffer-list)))) + +(with-current-buffer (get-lsp-install-buffer) + (while (not (string-match-p "^Comint" (thing-at-point 'line))) + (goto-char (point-max)) + (forward-line -1) + (sit-for 5) + (cl-incf timer 5) + (message "Waited %s..." timer))) + +(defconst server-install-path (lsp-package-path 'grammarly-ls) + "The server install location.") + +(unless (file-exists-p server-install-path) + (error "Failed to install server: %s" server-install-path) + (kill-emacs 1)) + +(message "Testing with a file...") + +(find-file "README.md") ; start lsp + +;;; activate.el ends here