什么選擇Atom?
開源–遵循MIT協議,代碼托管在github上面;
多平臺支持–支持MAC/WIN/LINUX;
豐富的插件庫和主題庫;
類sublime–風格和sublime text極其相似,不管是風格還是操作上,快捷鍵上一些是通用的;
采用包管理技術–采用了node.js來訪問文件系統和包管理;
強大的生命力–背靠github社區,這對于atom來說,可以注入源源不斷的生命力。
優秀插件推薦
1.emmet
這個插件如果用過sublime的都知道的。Emmet的前身是大名鼎鼎的Zen coding,如果你從事Web前端開發的話,對該插件一定不會陌生。它使用仿CSS選擇器的語法來生成代碼,大大提高了HTML/CSS代碼編寫的速度。
2. minimap
這個插件用過sublime的同學肯定不會陌生的。代碼地圖。
3. linter
這貨默認可以識別多門語言的錯誤,但是不細致,屬于主插件,可以針對性的安裝更細致的檢查插件,下面是可能用到的:linter-php,linter-jshint,linter-scss-lint,linter-less,linter-csslint
4. file-icons
這個插件可以對不同類型的文件顯示不同的文件圖標,包含在目錄樹里面和標簽頁里面都會顯示。
5. docblockr
這個插件可以幫助你快速的生成注釋,很多高級的編輯器都有這個功能,Atom怎么能少呢?
6. autoclose-html
自動閉合html標簽,很實用的功能,可以節省很多的時間。
7. highlight-line
高亮當前行,快速找到光標在什么地方。
8. highlight-selected
高亮當前所選文字,雙擊后全文這個詞或者變量都會高亮哦
9. autocomplete-plus
完善自帶autocomplete,可以安裝更細化的插件:autocomplete-php、autocomplete-python、autocomplete-paths、autocomplete-html、autocomplete-bibtex、autocomplete-css等等。
10. color-picker
顏色拾取插件,前端工作者的利器。
11. simplified-chinese-menu
Atom 的簡體中文漢化語言包,目前最全的漢化包。包含菜單漢化、右鍵菜單漢化以及設置漢化。
都說工欲善其事必先利其器,Python 的學習過程少不了 IDE 或者代碼編輯器,或者集成的開發編輯器(IDE)。
這些 Python 開發工具幫助開發者加快使用 Python 開發的速度,提高效率。高效的代碼編輯器或者 IDE 應該會提供插件,工具等能幫助開發者高效開發的特性。
這篇文章收集了一些對開發者非常有幫助的,最好的 10 款 Python IDE。
PyCharm 是 JetBrains 開發的 Python IDE。PyCharm用于一般IDE具備的功能,比如, 調試、語法高亮、Project管理、代碼跳轉、智能提示、自動完成、單元測試、版本控制……另外,PyCharm還提供了一些很好的功能用于Django開發,同時支持Google App Engine,更酷的是,PyCharm支持IronPython!
曾有人開玩笑說,如果生成一段隨機字符串,有人回答給他一個Vim 編輯器就可以了。Vim 雖然使用門檻高,但是一旦你學會了,寫代碼的效率杠杠的,Vim 是高級文本編輯器,旨在提供實際的 Unix 編輯器’Vi’功能,支持更多更完善的特性集。Vim 不需要花費太多的學習時間,一旦你需要一個無縫的編程體驗,那么就會把 Vim 集成到你的工作流中。這是黑客最喜歡的編輯器之一。
Eclipse 曾今是非常流行的 IDE,而且已經有了很久的歷史。雖然現在慢慢地被 jetbrains 家族替代了,但是,瘦死的駱駝比馬大,Eclipse with Pydev 允許開發者創建有用和交互式的 Web 應用。PyDev 是 Eclipse 開發 Python 的 IDE,支持 Python,Jython和 IronPython 的開發。
Sublime Text 是開發者中最流行的編輯器之一,多功能,支持多種語言,而且在開發者社區非常受歡迎。Sublime 有自己的包管理器,開發者可以使用TA來安裝組件,插件和額外的樣式,所有這些都能提升你的編碼體驗。雖然作為收費軟件,良心的是你不需要付費也能正常使用,只不過是不是給你彈出一個購買的窗口,有錢了還是要支持一下正版軟件。
Visual Studio Code (VSCode)為MS所開發的code editing tool,免費且開源,并支持Windows,Mac OS,Linux。VSCode像是精簡版的Visual Studio,升級版的Sublime。VSCode由于其非常的輕量,因此使用過程中非常的流暢,對于用戶不同的需要,可以自行下載需要的擴展(Extensions)來安裝。
對于配置Python開發環境來說,相比于Sublime,配置起來更加容易。VSCode配置完后的環境是可以直接進行可視化的Debug,再也不用打各種print或者用pdb調試命令了,回歸到Visual Studio里F10和F11。
Atom是由 GitHub 的程序員們打造的稱為“屬于21世紀”的代碼編輯器。它開源免費跨平臺(支持 Windows、Mac、Linux 三大桌面平臺),并且整合 GIT 并提供類似 SublimeText 的包管理功能,作為一個現代的代碼編輯器,Atom 支持各種編程語言的代碼高亮(HTML / CSS / Javascript / PHP / Python / C / C++ / Objective C / Java / JSON / Perl / CoffeeScript / Go / Sass / YAML / Markdown 等等)、 與大多數其他編輯器相比,Atom的語言支持已經算是覆蓋非常全面了。另外,它的代碼補全功能(也叫Snippets) 也非常好用,你只需輸入幾個字符即可展開成各種常用代碼,可以極大提高編程效率。
GNU Emacs 是可擴展,自定義的文本編輯器,甚至是更多的功能。Emacs 的核心是 Emacs Lisp 解析器,但是支持文本編輯。如果你已經使用過 Vim,可以嘗試一下 Emacs。
Spyder (前身是 Pydee) 是一個強大的交互式 Python 語言開發環境,提供高級的代碼編輯、交互測試、調試等特性,支持包括 Windows、Linux 和 OS X 系統。Spyder是Anaconda科學計算工具中默認的開發工具,做數據分析、搞機器學習的人喜歡這個工具
Thonny是基于python內置圖形庫tkinter開發出來的支持多平臺(windows,Mac,Linux)的python IDE,支持語法著色、代碼自動補全、debug等功能,如果你正在尋找一種“輕量級”的python IDE,那么可以試一試Thonny。
Wingware 的 Python IDE 兼容 Python 2.x 和 3.x,可以結合 Django, matplotlib, Zope, Plone, App Engine, PyQt, PySide, wxPython, PyGTK, Tkinter, mod_wsgi, pygame, Maya, MotionBuilder, NUKE, Blender 和其他 Python 框架使用。Wing 支持測試驅動開發,集成了單元測試,nose 和 Django 框架的執行和調試功能。Wing IDE 啟動和運行的速度都非常快,支持 Windows, Linux, OS X 和 Python versi。
tom是一個著名的開源編輯器,是由Chris Wanstrath在2008年作為其個人的編外項目發展而來。據說在今年(2022)年底,這款編輯器也將進入關停狀態。而且目前大部分程序員都把VS Code作為其最主要的開發工作,但是Atom本身的設計和代碼實現都是非常優秀的,通過閱讀它的源碼,我們還是可以學到很多相關的編程技巧。
atom的代碼結構非常清晰,整個項目可以分為兩個部分,一個是atom本身的代碼,另一個是atom的插件。atom本身的代碼又可以分為兩部分,一個是atom的核心業務邏輯,另一個是atom的UI代碼。核心業務邏輯主要
是用來設置環境變量,調度窗口、調度系統資源等等。UI代碼則主要負責處理atom的界面,比如菜單欄,工具欄,狀態欄等等。
作為使用electron框架編寫的應用程序,整體都是使用js來寫的(早期是使用coffee來編寫的),可以從其目錄中看到,整個項目的目錄結構如下:
|-src // 核心業務邏輯
|-|-main-process
|-|-|-atom-application.js
|-|-|-atom-environment.js
|-|-|-atom-window.js
|-static // UI代碼
|-packages // 其它擴展包
...
眾所周知,用Electron框架寫成的應用,都可以分為主線程和渲染進程。對應到atom中,主線程的代碼都是在src/main-process目錄下,而渲染線程的代碼則是直接src目錄下。靜態UI資源則在static目錄下。
我們先從主線程的入口代碼開始看起,代碼位于src/main-process/main.js路徑下:
// 命令行工具入口,
const args = yargs(process.argv)
// Don't handle --help or --version here; they will be handled later.
.help(false)
.version(false)
.alias('d', 'dev')
.alias('t', 'test')
.alias('r', 'resource-path').argv;
// 下面省略大量代碼,主要用于處理命令行參數,用來專門處理使用命令行打開atom的情況
// 真正的入口
const start = require(path.join(resourcePath, 'src', 'main-process', 'start'));
start(resourcePath, devResourcePath, startTime);
可以從上面代碼看出,其實真正的處理入口還是在start函數中(src/main-process/start.js):
module.exports = function start(resourcePath, devResourcePath, startTime) {
// 處理錯誤情況
process.on('uncaughtException', function(error = {}) {
});
process.on('unhandledRejection', function(error = {}) {
});
// 初始化各種參數
app.commandLine.appendSwitch('enable-experimental-web-platform-features');
const args = parseCommandLine(process.argv.slice(1));
const previousConsoleLog = console.log;
console.log = nslog;
args.resourcePath = normalizeDriveLetterName(resourcePath);
args.devResourcePath = normalizeDriveLetterName(devResourcePath);
atomPaths.setAtomHome(app.getPath('home'));
atomPaths.setUserData(app);
const config = getConfig();
const colorProfile = config.get('core.colorProfile');
if (colorProfile && colorProfile !== 'default') {
app.commandLine.appendSwitch('force-color-profile', colorProfile);
}
if (handleStartupEventWithSquirrel()) {
return;
} else if (args.test && args.mainProcess) {
// 處理測試情況
app.setPath(
'userData',
temp.mkdirSync('atom-user-data-dir-for-main-process-tests')
);
console.log = previousConsoleLog;
app.on('ready', function() {
const testRunner = require(path.join(
args.resourcePath,
'spec/main-process/mocha-test-runner'
));
testRunner(args.pathsToOpen);
});
return;
}
const releaseChannel = getReleaseChannel(app.getVersion());
let appUserModelId = 'com.squirrel.atom.' + process.arch;
if (releaseChannel !== 'stable') {
appUserModelId += `-${releaseChannel}`;
}
// 這個方法可以防止win10在任務欄中顯示重復的atom圖標
app.setAppUserModelId(appUserModelId);
app.on('open-file', addPathToOpen);
app.on('open-url', addUrlToOpen);
// 當應用關閉的時候,需要上報一些數據
app.on('will-finish-launching', () =>
startCrashReporter({
uploadToServer: config.get('core.telemetryConsent') === 'limited',
releaseChannel
})
);
if (args.userDataDir != null) {
app.setPath('userData', args.userDataDir);
} else if (args.test || args.benchmark || args.benchmarkTest) {
app.setPath('userData', temp.mkdirSync('atom-test-data'));
}
app.on('ready', function() {
app.removeListener('open-file', addPathToOpen);
app.removeListener('open-url', addUrlToOpen);
// 構造一個atomApplication對象
const AtomApplication = require(path.join(
args.resourcePath,
'src',
'main-process',
'atom-application'
));
// 并將之前的參數傳入
AtomApplication.open(args);
});
};
從上面代碼可以看出,前置處理也是各種參數的初始化,以及為了便于測試,做的一些定制處理。在應用初始化結束后,就會動態加載應用模塊,構造 AtomApplication 實例。可以注意到,這里使用按需加載的目的是希望能夠在需要的時候才會去加載對應的模塊,這樣可以減少內存的占用。
接著,我們來看一下atom-application.js的代碼,這塊代碼量比較大,是整個atom的核心代碼,我們先來看一下整體的結構:
// 是一個單列模式, 繼承自`EventEmitter`模塊,主要因為內部會大量應用事件處理機制來分發邏輯。
class AtomApplication extends EventEmitter {
static open(options) {
// 初始化一些參數
// 創建一個atomApplication對象
// 并將之前的參數傳入
return new AtomApplication(options);
}
exit(status) {
app.exit(status);
}
constructor(options){}
async initialize(options) {}
}
程序啟動的入口只有AtomApplication.open這一個方法,這個方法會創建一個AtomApplication對象,然后調用它的initialize方法,層層遞進,再調用創建窗口、加載配置等方法,最終完成程序的啟動。
其中,比較值得注意的是使用了一個叫做event-kit的模塊。它是一個事件處理器模塊,提供了一個事件處理器的抽象,可以讓我們更容易地處理事件。最重要的作用是它實現了CompositeDisposable類,可以在需要的時候,釋放資源。雖然javascript是一個有垃圾回收機制的語言,但是如果沒有手動釋放一些資源的話,會造成大量的內存占用。
在使用過程中,也十分簡單
class AtomApplication extends EventEmitter {
// 省略其它代碼
constructor(options) {
// 省略其它代碼
this.disposable = new CompositeDisposable();
}
async destroy() {
const windowsClosePromises = this.getAllWindows().map(window => {
window.close();
return window.closedPromise;
});
await Promise.all(windowsClosePromises);
// 在銷毀的時候統一釋放
this.disposable.dispose();
}
// 注冊事件處理函數
handleEvents() {
// 省略其它代碼,
// 在注冊事件回調的時候,直接將對象添加到disposable的依賴中去
this.disposable.add(
ipcHelpers.on(app, 'before-quit', async event => {...})
);
}
}
atom作為一個編輯器,它的擴展機制是非常重要的。和其他的IDE類似,擴展機制也是使用的微內核模式(或者插件模式)來實現。微內核架構是一種十分常見的軟件架構,它將應用系統分為兩個部分:一個微內核和一組外部的插件。微內核負責管理插件,提供插件之間的通信機制,以及提供一些基礎的服務。插件則負責提供具體的功能。這樣的架構可以讓我們更容易地擴展軟件的功能,而不需要修改軟件的核心代碼。
在atom中,插件主要是通過package類來實現的。package是atom擴展的基本單元,它可以包含一些功能,比如語法高亮、代碼提示、代碼格式化等等,也提供了讓第三方開發者擴展的能力。那么這些擴展是如何加載的呢?我們先來看一下package-manager.js的代碼:
// 可以加載、激活、停用、卸載包
// 加載包讀取并解析包的元數據和資源,例如快捷鍵、菜單、樣式表等
// 激活包注冊加載的資源并調用包的主模塊的`activate()`方法
// 停用包取消注冊包的資源并調用包的主模塊的`deactivate()`方法
// 卸載包從包管理器中完全移除
// 可以通過`core.disabledPackages`配置項和調用`enablePackage()/disablePackage()`方法來啟用/禁用包
class PackageManager {
preloadPackage(packageName, pack) {
...
}
loadPackages() {
...
}
enablePackage(packageName) {
...
}
// 觸發事件,用來注冊回調
onDidActivatePackage(callback) {
}
}
這個包管理器類PackageManager,可以管理擴展包的整個生命周期,主要負責包的加載、卸載、更新等操作。而所有的包都綁定在主內核的atom.packages這個全局變量上,我們可以通過這個變量來訪問應用上加載的所有擴展。
那么packageManager是如何負責管理包的安裝和卸載呢?:
class PackageManager {
constructor(packages) {
this.packages = packages;
}
getPackages() {
return this.packages;
}
getPackage(name) {
return this.packages.find(pkg => pkg.name === name);
}
// 禁用包,從內存中將包去除,然后通知應用程序或者擴展來執行禁用操作
async deactivatePackage(name, suppressSerialization) {
const pack = this.getLoadedPackage(name);
if (pack == null) {
return;
}
if (!suppressSerialization && this.isPackageActive(pack.name)) {
this.serializePackage(pack);
}
const deactivationResult = pack.deactivate();
if (deactivationResult && typeof deactivationResult.then === 'function') {
await deactivationResult;
}
delete this.activePackages[pack.name];
delete this.activatingPackages[pack.name];
this.emitter.emit('did-deactivate-package', pack);
}
}
比如在擴展ui-watcher中,就可以在監聽到did-deactivate-package事件后,執行一些清理操作:
watchForPackageChanges() {
this.subscriptions.add(
atom.packages.onDidDeactivatePackage(pack => {
// This only handles packages - onDidChangeActiveThemes handles themes
const watcher = this.watchedPackages.get(pack.name);
if (watcher) watcher.destroy();
this.watchedPackages.delete(pack.name);
})
);
}
Package類,則包含了包的基礎信息,包括鍵位設置、配置、樣式等,并且有完整的生命周期。
class Package {
constructor(params) {
this.config = params.config;
this.packageManager = params.packageManager;
this.styleManager = params.styleManager;
this.commandRegistry = params.commandRegistry;
this.keymapManager = params.keymapManager;
this.notificationManager = params.notificationManager;
this.grammarRegistry = params.grammarRegistry;
this.themeManager = params.themeManager;
this.menuManager = params.menuManager;
this.contextMenuManager = params.contextMenuManager;
this.deserializerManager = params.deserializerManager;
this.viewRegistry = params.viewRegistry;
this.emitter = new Emitter();
// 此處省略大量的細節
}
preload() {
// do something
}
load() {
// do something
}
unload() {
// do something
}
activate() {
// do something
}
deactivate() {
// do something
}
finishLoading() {
// do something
}
}
Package(擴展)實例本身要和主應用進行通信,atom是直接通過全局對象的方式進行調用的,這樣做的好處是不用考慮通信的問題,但是也有一些弊端,比如不方便重構等。
在應用入口處,會將PackageManager實例掛載在應用實例上。后續我們可以通過atom.packages來訪問包管理器實例,從而獲取包的信息。
// atom-application.js
this.packages = new PackageManager({
... // 一堆的配置
});
this.packages.initialize(...);
而在渲染進程中,可以通過在window上掛載atom對象來訪問包管理器實例,從而獲取所有擴展包的信息,進行預加載操作。
// initialize-application-window.js
/ 初始化 AtomEnvironment
global.atom = new AtomEnvironment({
clipboard,
applicationDelegate: new ApplicationDelegate(),
enablePersistence: true
});
TextEditor.setScheduler(global.atom.views);
// 初始化應用窗口
global.atom.preloadPackages();
// ... 省略大量代碼
module.exports = function({ blobStore }) {
// 省略大量代碼
// 在startEditorWindows內部,當窗口初始化完成后,會正式調用`loadPackages`方法來加載所有的擴展包
return global.atom.startEditorWindow().then(function() {
// Workaround for focus getting cleared upon window creation
const windowFocused = function() {
window.removeEventListener('focus', windowFocused);
setTimeout(() => document.querySelector('atom-workspace').focus(), 0);
};
window.addEventListener('focus', windowFocused);
ipcRenderer.on('environment', (event, env) => updateProcessEnv(env));
});
}
總體而言,atom的擴展機制還是比較簡單的,在各種擴展的生命周期中,都可以通過事件來進行通信,從而實現各種功能。這樣一種實現,其實也可以在我們日常工作過程中加以借鑒。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。