mirror of
https://github.com/marktext/marktext.git
synced 2025-05-04 16:50:32 +08:00
fix: sidebar tree view (#764)
This commit is contained in:
parent
2007fc59e9
commit
eff54dcaa1
@ -190,10 +190,7 @@ class AppWindow {
|
|||||||
const unwatcher = this.watcher.watch(win, pathname)
|
const unwatcher = this.watcher.watch(win, pathname)
|
||||||
this.windows.get(win.id).watchers.push(unwatcher)
|
this.windows.get(win.id).watchers.push(unwatcher)
|
||||||
try {
|
try {
|
||||||
win.webContents.send('AGANI::open-project', {
|
win.webContents.send('AGANI::open-project', pathname)
|
||||||
name: path.basename(pathname),
|
|
||||||
pathname
|
|
||||||
})
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
log(err)
|
log(err)
|
||||||
}
|
}
|
||||||
|
@ -19,8 +19,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import path from 'path'
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import { fileMixins } from '../../mixins'
|
import { fileMixins } from '../../mixins'
|
||||||
|
import { PATH_SEPARATOR } from '../../config'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [fileMixins],
|
mixins: [fileMixins],
|
||||||
@ -36,14 +38,21 @@
|
|||||||
tabs: state => state.editor.tabs,
|
tabs: state => state.editor.tabs,
|
||||||
currentFile: state => state.editor.currentFile
|
currentFile: state => state.editor.currentFile
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
// Return filename without extension.
|
||||||
filename () {
|
filename () {
|
||||||
return this.file.name.split('.')[0]
|
return path.basename(this.file.name, path.extname(this.file.name))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Return the filename extension or null.
|
||||||
extension () {
|
extension () {
|
||||||
return `.${this.file.name.split('.')[1]}`
|
return path.extname(this.file.name)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Return the parent directory with trailing path separator.
|
||||||
|
// NOTE: Parent from "Z:\" is "Z:\" on Windows!
|
||||||
parent () {
|
parent () {
|
||||||
return this.file.pathname.match(/(?:^|\/)([^/]+)\/[^/]+\.[^/]+$/)[1] + '/'
|
return path.join(path.dirname(this.file.pathname), PATH_SEPARATOR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,7 @@
|
|||||||
import { remote } from 'electron'
|
import { remote } from 'electron'
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import { minimizePath, restorePath, maximizePath, closePath } from '../assets/window-controls.js'
|
import { minimizePath, restorePath, maximizePath, closePath } from '../assets/window-controls.js'
|
||||||
|
import { PATH_SEPARATOR } from '../config'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
@ -134,7 +135,7 @@
|
|||||||
}),
|
}),
|
||||||
paths () {
|
paths () {
|
||||||
if (!this.pathname) return []
|
if (!this.pathname) return []
|
||||||
const pathnameToken = this.pathname.split('/').filter(i => i)
|
const pathnameToken = this.pathname.split(PATH_SEPARATOR).filter(i => i)
|
||||||
return pathnameToken.slice(0, pathnameToken.length - 1).slice(-3)
|
return pathnameToken.slice(0, pathnameToken.length - 1).slice(-3)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
export const PATH_SEPARATOR = path.sep
|
||||||
|
|
||||||
export const THEME_LINK_ID = 'ag-theme'
|
export const THEME_LINK_ID = 'ag-theme'
|
||||||
export const COMMON_STYLE_ID = 'ag-common-style'
|
export const COMMON_STYLE_ID = 'ag-common-style'
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import { ipcRenderer, shell } from 'electron'
|
|||||||
import { addFile, unlinkFile, changeFile, addDirectory, unlinkDirectory } from './treeCtrl'
|
import { addFile, unlinkFile, changeFile, addDirectory, unlinkDirectory } from './treeCtrl'
|
||||||
import bus from '../bus'
|
import bus from '../bus'
|
||||||
import { create, paste, rename } from '../util/fileSystem'
|
import { create, paste, rename } from '../util/fileSystem'
|
||||||
|
import { PATH_SEPARATOR } from '../config'
|
||||||
import notice from '../services/notification'
|
import notice from '../services/notification'
|
||||||
import { getFileStateFromData } from './help'
|
import { getFileStateFromData } from './help'
|
||||||
|
|
||||||
@ -36,9 +37,17 @@ const getters = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
SET_PROJECT_TREE (state, { pathname, name }) {
|
SET_PROJECT_TREE (state, pathname) {
|
||||||
|
let name = path.basename(pathname)
|
||||||
|
if (!name) {
|
||||||
|
// Root directory such "/" or "C:\"
|
||||||
|
name = pathname
|
||||||
|
}
|
||||||
|
|
||||||
state.projectTree = {
|
state.projectTree = {
|
||||||
pathname,
|
// Root full path
|
||||||
|
pathname: path.normalize(pathname),
|
||||||
|
// Root directory name
|
||||||
name,
|
name,
|
||||||
isDirectory: true,
|
isDirectory: true,
|
||||||
isFile: false,
|
isFile: false,
|
||||||
@ -105,12 +114,12 @@ const mutations = {
|
|||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
LISTEN_FOR_LOAD_PROJECT ({ commit, dispatch }) {
|
LISTEN_FOR_LOAD_PROJECT ({ commit, dispatch }) {
|
||||||
ipcRenderer.on('AGANI::open-project', (e, { pathname, name }) => {
|
ipcRenderer.on('AGANI::open-project', (e, pathname) => {
|
||||||
// Initialize editor and show empty/new tab
|
// Initialize editor and show empty/new tab
|
||||||
dispatch('NEW_BLANK_FILE')
|
dispatch('NEW_BLANK_FILE')
|
||||||
|
|
||||||
dispatch('INIT_STATUS', true)
|
dispatch('INIT_STATUS', true)
|
||||||
commit('SET_PROJECT_TREE', { pathname, name })
|
commit('SET_PROJECT_TREE', pathname)
|
||||||
commit('SET_LAYOUT', {
|
commit('SET_LAYOUT', {
|
||||||
rightColumn: 'files',
|
rightColumn: 'files',
|
||||||
showSideBar: true,
|
showSideBar: true,
|
||||||
@ -189,7 +198,7 @@ const actions = {
|
|||||||
const { pathname, isDirectory } = state.activeItem
|
const { pathname, isDirectory } = state.activeItem
|
||||||
const dirname = isDirectory ? pathname : path.dirname(pathname)
|
const dirname = isDirectory ? pathname : path.dirname(pathname)
|
||||||
if (clipboard) {
|
if (clipboard) {
|
||||||
clipboard.dest = dirname + '/' + path.basename(clipboard.src)
|
clipboard.dest = dirname + PATH_SEPARATOR + path.basename(clipboard.src)
|
||||||
paste(clipboard)
|
paste(clipboard)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
commit('SET_CLIPBOARD', null)
|
commit('SET_CLIPBOARD', null)
|
||||||
@ -232,7 +241,7 @@ const actions = {
|
|||||||
RENAME_IN_SIDEBAR ({ commit, state }, name) {
|
RENAME_IN_SIDEBAR ({ commit, state }, name) {
|
||||||
const src = state.renameCache
|
const src = state.renameCache
|
||||||
const dirname = path.dirname(src)
|
const dirname = path.dirname(src)
|
||||||
const dest = dirname + '/' + name
|
const dest = dirname + PATH_SEPARATOR + name
|
||||||
rename(src, dest)
|
rename(src, dest)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
commit('RENAME_IF_NEEDED', { src, dest })
|
commit('RENAME_IF_NEEDED', { src, dest })
|
||||||
|
@ -1,34 +1,43 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { getUniqueId } from '../util'
|
import { getUniqueId } from '../util'
|
||||||
|
import { PATH_SEPARATOR } from '../config'
|
||||||
|
|
||||||
const pathSeparator = path.sep
|
/**
|
||||||
const isWindows = process.platform === 'win32'
|
* Return all sub-directories relative to the root directory.
|
||||||
|
*
|
||||||
const getPathArr = (projectName, pathname) => {
|
* @param {string} rootPath Root directory path
|
||||||
const reUnix = /^\/+(.*)/
|
* @param {string} pathname Full directory path
|
||||||
const reWindows = /^\\+(.*)/
|
* @returns {Array<string>} Sub-directories relative to root.
|
||||||
let [prePath, partPath] = pathname.split(projectName)
|
*/
|
||||||
partPath = partPath.replace(isWindows ? reWindows : reUnix, (_, p1) => p1)
|
const getSubdirectoriesFromRoot = (rootPath, pathname) => {
|
||||||
prePath += projectName
|
if (!path.isAbsolute(pathname)) {
|
||||||
const tokens = partPath ? partPath.split(pathSeparator) : []
|
throw new Error('Invalid path!')
|
||||||
|
}
|
||||||
return { prePath, tokens }
|
const relativePath = path.relative(rootPath, pathname)
|
||||||
|
return relativePath ? relativePath.split(PATH_SEPARATOR) : []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new file to the tree list.
|
||||||
|
*
|
||||||
|
* @param {*} tree Root file tree
|
||||||
|
* @param {*} file The file that should be added
|
||||||
|
*/
|
||||||
export const addFile = (tree, file) => {
|
export const addFile = (tree, file) => {
|
||||||
const { pathname, name } = file
|
const { pathname, name } = file
|
||||||
const dirname = path.dirname(pathname)
|
const dirname = path.dirname(pathname)
|
||||||
let { prePath, tokens } = getPathArr(tree.name, dirname)
|
const subDirectories = getSubdirectoriesFromRoot(tree.pathname, dirname)
|
||||||
let folder = tree
|
|
||||||
let folders = tree.folders
|
|
||||||
|
|
||||||
for (const token of tokens) {
|
let currentPath = tree.pathname
|
||||||
let childFolder = folders.find(f => f.name === token)
|
let currentFolder = tree
|
||||||
|
let currentSubFolders = tree.folders
|
||||||
|
for (const directoryName of subDirectories) {
|
||||||
|
let childFolder = currentSubFolders.find(f => f.name === directoryName)
|
||||||
if (!childFolder) {
|
if (!childFolder) {
|
||||||
childFolder = {
|
childFolder = {
|
||||||
id: getUniqueId(),
|
id: getUniqueId(),
|
||||||
pathname: `${prePath}${pathSeparator}${token}`,
|
pathname: `${currentPath}${PATH_SEPARATOR}${directoryName}`,
|
||||||
name: token,
|
name: directoryName,
|
||||||
isCollapsed: true,
|
isCollapsed: true,
|
||||||
isDirectory: true,
|
isDirectory: true,
|
||||||
isFile: false,
|
isFile: false,
|
||||||
@ -36,30 +45,39 @@ export const addFile = (tree, file) => {
|
|||||||
folders: [],
|
folders: [],
|
||||||
files: []
|
files: []
|
||||||
}
|
}
|
||||||
folders.push(childFolder)
|
currentSubFolders.push(childFolder)
|
||||||
}
|
}
|
||||||
prePath = `${prePath}${pathSeparator}${token}`
|
|
||||||
folder = childFolder
|
currentPath = `${currentPath}${PATH_SEPARATOR}${directoryName}`
|
||||||
folders = childFolder.folders
|
currentFolder = childFolder
|
||||||
|
currentSubFolders = childFolder.folders
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!folder.files.find(f => f.name === name)) {
|
// Add file to related directory
|
||||||
|
if (!currentFolder.files.find(f => f.name === name)) {
|
||||||
file.id = getUniqueId()
|
file.id = getUniqueId()
|
||||||
folder.files.push(file)
|
currentFolder.files.push(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new directory to the tree list.
|
||||||
|
*
|
||||||
|
* @param {*} tree Root file tree
|
||||||
|
* @param {*} dir The directory that should be added
|
||||||
|
*/
|
||||||
export const addDirectory = (tree, dir) => {
|
export const addDirectory = (tree, dir) => {
|
||||||
let { prePath, tokens } = getPathArr(tree.name, dir.pathname)
|
const subDirectories = getSubdirectoriesFromRoot(tree.pathname, dir.pathname)
|
||||||
let folders = tree.folders
|
|
||||||
|
|
||||||
for (const token of tokens) {
|
let currentPath = tree.pathname
|
||||||
let childFolder = folders.find(f => f.name === token)
|
let currentSubFolders = tree.folders
|
||||||
|
for (const directoryName of subDirectories) {
|
||||||
|
let childFolder = currentSubFolders.find(f => f.name === directoryName)
|
||||||
if (!childFolder) {
|
if (!childFolder) {
|
||||||
childFolder = {
|
childFolder = {
|
||||||
id: getUniqueId(),
|
id: getUniqueId(),
|
||||||
pathname: `${prePath}${pathSeparator}${token}`,
|
pathname: `${currentPath}${PATH_SEPARATOR}${directoryName}`,
|
||||||
name: token,
|
name: directoryName,
|
||||||
isCollapsed: true,
|
isCollapsed: true,
|
||||||
isDirectory: true,
|
isDirectory: true,
|
||||||
isFile: false,
|
isFile: false,
|
||||||
@ -67,70 +85,86 @@ export const addDirectory = (tree, dir) => {
|
|||||||
folders: [],
|
folders: [],
|
||||||
files: []
|
files: []
|
||||||
}
|
}
|
||||||
folders.push(childFolder)
|
currentSubFolders.push(childFolder)
|
||||||
}
|
}
|
||||||
prePath = `${prePath}${pathSeparator}${token}`
|
|
||||||
folders = childFolder.folders
|
currentPath = `${currentPath}${PATH_SEPARATOR}${directoryName}`
|
||||||
|
currentSubFolders = childFolder.folders
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the given file from the tree list.
|
||||||
|
*
|
||||||
|
* @param {*} tree Root file tree
|
||||||
|
* @param {*} file The file that should be deleted
|
||||||
|
*/
|
||||||
export const unlinkFile = (tree, file) => {
|
export const unlinkFile = (tree, file) => {
|
||||||
const { pathname } = file
|
const { pathname } = file
|
||||||
const dirname = path.dirname(pathname)
|
const dirname = path.dirname(pathname)
|
||||||
let { tokens } = getPathArr(tree.name, dirname)
|
const subDirectories = getSubdirectoriesFromRoot(tree.pathname, dirname)
|
||||||
|
|
||||||
let folder = tree
|
let currentFolder = tree
|
||||||
let folders = tree.folders
|
let currentSubFolders = tree.folders
|
||||||
|
for (const directoryName of subDirectories) {
|
||||||
for (const token of tokens) {
|
const childFolder = currentSubFolders.find(f => f.name === directoryName)
|
||||||
const childFolder = folders.find(f => f.name === token)
|
|
||||||
if (!childFolder) return
|
if (!childFolder) return
|
||||||
folder = childFolder
|
currentFolder = childFolder
|
||||||
folders = childFolder.folders
|
currentSubFolders = childFolder.folders
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = folder.files.findIndex(f => f.pathname === pathname)
|
const index = currentFolder.files.findIndex(f => f.pathname === pathname)
|
||||||
|
if (index !== -1) {
|
||||||
if (index > -1) {
|
currentFolder.files.splice(index, 1)
|
||||||
folder.files.splice(index, 1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a given file in the tree list.
|
||||||
|
*
|
||||||
|
* @param {*} tree Root file tree
|
||||||
|
* @param {*} file The file that was changed
|
||||||
|
*/
|
||||||
export const changeFile = (tree, file) => {
|
export const changeFile = (tree, file) => {
|
||||||
const { pathname, data } = file
|
const { pathname, data } = file
|
||||||
const dirname = path.dirname(pathname)
|
const dirname = path.dirname(pathname)
|
||||||
let { tokens } = getPathArr(tree.name, dirname)
|
const subDirectories = getSubdirectoriesFromRoot(tree.pathname, dirname)
|
||||||
|
|
||||||
let folder = tree
|
let currentFolder = tree
|
||||||
let folders = tree.folders
|
let currentSubFolders = tree.folders
|
||||||
|
for (const directoryName of subDirectories) {
|
||||||
for (const token of tokens) {
|
const childFolder = currentSubFolders.find(f => f.name === directoryName)
|
||||||
const childFolder = folders.find(f => f.name === token)
|
|
||||||
if (!childFolder) return
|
if (!childFolder) return
|
||||||
folder = childFolder
|
currentFolder = childFolder
|
||||||
folders = childFolder.folders
|
currentSubFolders = childFolder.folders
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = folder.files.findIndex(f => f.pathname === pathname)
|
const index = currentFolder.files.findIndex(f => f.pathname === pathname)
|
||||||
folder.files[index].data = data
|
if (index !== -1) {
|
||||||
|
currentFolder.files[index].data = data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the given directory from the tree list.
|
||||||
|
*
|
||||||
|
* @param {*} tree Root file tree
|
||||||
|
* @param {*} dir The directory that should be deleted
|
||||||
|
*/
|
||||||
export const unlinkDirectory = (tree, dir) => {
|
export const unlinkDirectory = (tree, dir) => {
|
||||||
const { pathname } = dir
|
const { pathname } = dir
|
||||||
const { tokens } = getPathArr(tree.name, pathname)
|
const subDirectories = getSubdirectoriesFromRoot(tree.pathname, pathname)
|
||||||
|
|
||||||
tokens.pop()
|
subDirectories.pop()
|
||||||
let folders = tree.folders
|
let currentFolder = tree.folders
|
||||||
|
for (const directoryName of subDirectories) {
|
||||||
for (const token of tokens) {
|
const childFolder = currentFolder.find(f => f.name === directoryName)
|
||||||
const childFolder = folders.find(f => f.name === token)
|
|
||||||
if (!childFolder) return
|
if (!childFolder) return
|
||||||
folders = childFolder.folders
|
currentFolder = childFolder.folders
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = folders.findIndex(f => f.pathname === pathname)
|
const index = currentFolder.findIndex(f => f.pathname === pathname)
|
||||||
|
if (index !== -1) {
|
||||||
if (index > -1) {
|
currentFolder.splice(index, 1)
|
||||||
folders.splice(index, 1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user