mirror of
https://github.com/chylex/Blog.git
synced 2025-07-04 12:39:00 +02:00
Compare commits
No commits in common. "7af4a3bf1b2e92322cf92888239cc499ddffc2a8" and "cc9f39b22383f8245368db75a5bd2a9b511f5349" have entirely different histories.
7af4a3bf1b
...
cc9f39b223
@ -74,12 +74,12 @@
|
|||||||
<span class="site-footer-owner">
|
<span class="site-footer-owner">
|
||||||
<a href="https://chylex.com">My Website</a>
|
<a href="https://chylex.com">My Website</a>
|
||||||
·
|
·
|
||||||
<a href="https://chylex.bsky.social/">Bluesky</a>
|
<a href="https://twitter.com/chylexmc">Twitter</a>
|
||||||
·
|
|
||||||
<a href="https://mastodon.chylex.com/@chylex">Mastodon</a>
|
|
||||||
·
|
·
|
||||||
<a href="https://github.com/chylex">GitHub</a>
|
<a href="https://github.com/chylex">GitHub</a>
|
||||||
·
|
·
|
||||||
|
<a href="https://patreon.com/chylex">Patreon</a>
|
||||||
|
·
|
||||||
<a href="https://ko-fi.com/chylex">Ko-fi</a>
|
<a href="https://ko-fi.com/chylex">Ko-fi</a>
|
||||||
·
|
·
|
||||||
<a href="{{ '/feed.xml' | relative_url }}">RSS</a>
|
<a href="{{ '/feed.xml' | relative_url }}">RSS</a>
|
||||||
|
@ -1,118 +0,0 @@
|
|||||||
---
|
|
||||||
title: "JetBrains Writerside EAP Expiration Bypass"
|
|
||||||
subtitle: "%pub"
|
|
||||||
date: 2025-04-22
|
|
||||||
commentid: 5
|
|
||||||
---
|
|
||||||
|
|
||||||
JetBrains has [discontinued the Writerside IDE](https://blog.jetbrains.com/writerside/2025/03/sunsetting-writerside-ide/) in favor of the Writerside plugin. Since the IDE has always been in the Early Access Program (EAP), its builds expire after 30 days, meaning they can’t be used indefinitely. Personally, I prefer a separate IDE for writing documentation, and the plugin has some UX annoyances I don't want to deal with, so let's bypass the EAP expiration!
|
|
||||||
|
|
||||||
I’m using Writerside 2024.3 EAP (243.22562.371), but the guide should give you enough information to adapt to changes in other versions.
|
|
||||||
|
|
||||||
# Finding the Expiration Check
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Writerside, like other JetBrains IDEs, uses Java Swing. That makes it easy to find the code used to show a modal dialog — such as the one warning you that the EAP build expired. Every modal dialog spawns an event loop, which will appear in the stack trace.
|
|
||||||
|
|
||||||
Once Writerside is running and the "Writerside EAP Build Expired" dialog is visible, find the process ID (PID) and run `jstack <pid>` to dump the stack trace. You can use `jstack` from Writerside's installation directory:
|
|
||||||
|
|
||||||
```cmd
|
|
||||||
%LOCALAPPDATA%\Programs\Writerside\jbr\bin\jstack.exe 12345
|
|
||||||
```
|
|
||||||
|
|
||||||
Look for the `AWT-EventQueue-0` thread. I highlighted the important parts of the stack trace with arrows.
|
|
||||||
|
|
||||||
```stacktrace
|
|
||||||
"AWT-EventQueue-0" #72 [21032] prio=6 os_prio=0 cpu=1031.25ms elapsed=323.16s tid=0x0000019c1a2f3c40 nid=21032 waiting on condition [0x0000009afb8fc000]
|
|
||||||
java.lang.Thread.State: WAITING (parking)
|
|
||||||
(unimportant lines)
|
|
||||||
at java.awt.EventQueue.getNextEvent(java.desktop/EventQueue.java:573)
|
|
||||||
at com.intellij.ide.IdeEventQueue.getNextEvent(IdeEventQueue.kt:466)
|
|
||||||
at java.awt.EventDispatchThread.pumpOneEventForFilters(java.desktop/EventDispatchThread.java:194)
|
|
||||||
at java.awt.EventDispatchThread.pumpEventsForFilter(java.desktop/EventDispatchThread.java:128)
|
|
||||||
at java.awt.EventDispatchThread.pumpEventsForFilter(java.desktop/EventDispatchThread.java:121)
|
|
||||||
at java.awt.WaitDispatchSupport$2.run(java.desktop/WaitDispatchSupport.java:191)
|
|
||||||
at java.awt.WaitDispatchSupport$4.run(java.desktop/WaitDispatchSupport.java:236)
|
|
||||||
at java.awt.WaitDispatchSupport$4.run(java.desktop/WaitDispatchSupport.java:234)
|
|
||||||
at java.security.AccessController.executePrivileged(java.base@21.0.5/AccessController.java:778)
|
|
||||||
at java.security.AccessController.doPrivileged(java.base@21.0.5/AccessController.java:319)
|
|
||||||
at java.awt.WaitDispatchSupport.enter(java.desktop/WaitDispatchSupport.java:234)
|
|
||||||
--> at java.awt.Dialog.show(java.desktop/Dialog.java:1079)
|
|
||||||
at com.intellij.openapi.ui.impl.DialogWrapperPeerImpl$MyDialog.show(DialogWrapperPeerImpl.java:890)
|
|
||||||
at com.intellij.openapi.ui.impl.DialogWrapperPeerImpl.show(DialogWrapperPeerImpl.java:472)
|
|
||||||
at com.intellij.openapi.ui.DialogWrapper.doShow(DialogWrapper.java:1772)
|
|
||||||
at com.intellij.openapi.ui.DialogWrapper.show(DialogWrapper.java:1721)
|
|
||||||
at com.intellij.ui.messages.AlertMessagesManager.showMessageDialog(AlertMessagesManager.kt:70)
|
|
||||||
at com.intellij.ui.messages.MessagesServiceImpl.showMessageDialog(MessagesServiceImpl.java:54)
|
|
||||||
at com.intellij.openapi.ui.Messages.showDialog(Messages.java:274)
|
|
||||||
at com.intellij.openapi.ui.Messages.showDialog(Messages.java:290)
|
|
||||||
at com.intellij.openapi.ui.Messages.showDialog(Messages.java:305)
|
|
||||||
--> at com.intellij.ide.V.a.VP.p(VP.java:320)
|
|
||||||
--> at com.intellij.ide.V.a.VP.H(VP.java:308)
|
|
||||||
at com.intellij.ide.V.a.VP$$Lambda/0x0000019ba1c3ab78.run(Unknown Source)
|
|
||||||
(unimportant lines)
|
|
||||||
```
|
|
||||||
|
|
||||||
The top of the stack trace is processing an event queue spawned by `java.awt.Dialog.show`. A few lines below is the main point of interest — two methods related to the expiration check, that made the dialog appear: `com.intellij.ide.V.a.VP.p` and `com.intellij.ide.V.a.VP.H`.
|
|
||||||
|
|
||||||
# Patching the Expiration Check
|
|
||||||
|
|
||||||
To remove the expiration check, I will use [Recaf](https://github.com/Col-E/Recaf) 2.21.4.
|
|
||||||
|
|
||||||
To save you some time, the `VP` class is found inside `Writerside/lib/product.jar`. On Windows, this would be in `%LOCALAPPDATA%\Programs\Writerside\lib\`.
|
|
||||||
|
|
||||||
Open `product.jar` in Recaf and search for `com/intellij/ide/V/a/VP`.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Opening the class will attempt to decompile it. Due to heavy obfuscation, the decompiled code or line numbers from the stack trace are mostly useless, so right-click the `VP` tab and switch to the Table view.
|
|
||||||
|
|
||||||
The `p` method that calls `Messages.showDialog` can be difficult to find since its name is shared with several other methods, but the `H` method is unique.
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
Right-click the `H` method, select "Edit with assembler", and replace everything after the first line with `RETURN`. Press `Ctrl+S` to save, and close the editor.
|
|
||||||
|
|
||||||
```
|
|
||||||
DEFINE PRIVATE SYNTHETIC H()V
|
|
||||||
RETURN
|
|
||||||
```
|
|
||||||
|
|
||||||
> In Recaf 4, you can use the "Edit - Make no-op" option in the context menu instead.
|
|
||||||
|
|
||||||
Use "File - Export program" to export the patched `product.jar`, and replace the original file. You should now be able to use Writerside forever!
|
|
||||||
|
|
||||||
# Addendum: Fixing the Git Tool Window
|
|
||||||
|
|
||||||
The 243.22562.371 build of Writerside has an annoying bug, where the Git tool window doesn't show a diff preview depending on its location. This was caused by [commit c456622](https://github.com/JetBrains/intellij-community/commit/c4566222c3c4ca2bb08080ae3d476d65331a8ec4), and is also easy to fix using Recaf.
|
|
||||||
|
|
||||||
The buggy code is in `Writerside/lib/modules/intellij.platform.vcs.impl.jar`, in these two classes:
|
|
||||||
- `com/intellij/openapi/vcs/changes/ChangesViewManager$ChangesViewToolWindowPanel`
|
|
||||||
- `com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager$ShelfToolWindowPanel`
|
|
||||||
|
|
||||||
Both classes have an `updatePanelLayout` method that calls `ChangesViewContentManager.isToolWindowTabVertical`, stores the result in a local variable, and hides the preview diff if the variable is `true`.
|
|
||||||
|
|
||||||
```java
|
|
||||||
boolean isVertical = isToolWindowTabVertical(myProject, LOCAL_CHANGES);
|
|
||||||
boolean hasSplitterPreview = !isVertical;
|
|
||||||
```
|
|
||||||
|
|
||||||
Edit each `updatePanelLayout` method with assembler and search for the `isToolWindowTabVertical` call. The surrounding bytecode instructions are almost the same in both classes.
|
|
||||||
|
|
||||||
```
|
|
||||||
ALOAD this
|
|
||||||
GETFIELD com/intellij/openapi/vcs/changes/ChangesViewManager$ChangesViewToolWindowPanel.myProject ...
|
|
||||||
LDC "Local Changes"
|
|
||||||
INVOKESTATIC com/intellij/openapi/vcs/changes/ui/ChangesViewContentManager.isToolWindowTabVertical(...)Z
|
|
||||||
ISTORE isVertical
|
|
||||||
```
|
|
||||||
|
|
||||||
Replace these instructions — except for the `ISTORE` instruction — with `ICONST_0`, and save.
|
|
||||||
|
|
||||||
```
|
|
||||||
ICONST_0
|
|
||||||
ISTORE isVertical
|
|
||||||
```
|
|
||||||
|
|
||||||
This forces the `isVertical` variable to always be `false`, preventing the preview diff from being hidden.
|
|
Binary file not shown.
Before Width: | Height: | Size: 6.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 17 KiB |
Loading…
Reference in New Issue
Block a user